Added focus handling, horizontal and vertical autoscrolling, extended
authorLars Hamann <lars@src.gnome.org>
Wed, 15 Jul 1998 23:40:00 +0000 (23:40 +0000)
committerLars Hamann <lars@src.gnome.org>
Wed, 15 Jul 1998 23:40:00 +0000 (23:40 +0000)
* gtk/gtkctree.h :
* gtk/gtkctree.c :
  Added focus handling, horizontal and vertical autoscrolling,
  extended Selection Mode, key bindings
  Added gtk_ctree_toggle_expansion & gtk_ctree_toggle_expansion_recursive
  Changed gtk_ctree_scroll_to to gtk_ctree_moveto.
  Removed gtk_ctree_clear.

* gtk/gtkclist.h :
* gtk/gtkclist.c :
  Added focus handling, horizontal and vertical autoscrolling,
  extended Selection Mode, key bindings

14 files changed:
ChangeLog
ChangeLog.pre-2-0
ChangeLog.pre-2-10
ChangeLog.pre-2-2
ChangeLog.pre-2-4
ChangeLog.pre-2-6
ChangeLog.pre-2-8
TODO
gtk/gtkclist.c
gtk/gtkclist.h
gtk/gtkctree.c
gtk/gtkctree.h
gtk/testgtk.c
tests/testgtk.c

index 753c024af7f38b873a33cf1127694eaa0f602a1e..43a7f540cebb0aa415dac7ff1ddce8365ec1f5e3 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+Thu Jul 16 01:10:02 1998  Lars Hamann  <lars@gtk.org>
+
+       * gtk/gtkctree.h :
+       * gtk/gtkctree.c :
+       Added focus handling, horizontal and vertical autoscrolling,
+       extended Selection Mode, key bindings
+       Added gtk_ctree_toggle_expansion & gtk_ctree_toggle_expansion_recursive
+       Changed gtk_ctree_scroll_to to gtk_ctree_moveto.
+       Removed gtk_ctree_clear.
+       
+       * gtk/gtkclist.h :
+       * gtk/gtkclist.c : 
+       Added focus handling, horizontal and vertical autoscrolling,
+       extended Selection Mode, key bindings
+
 Wed Jul 15 12:39:27 1998  Raph Levien  <raph@acm.org>
 
        * This checkin integrates GdkRgb version 0.0.3 into the Gtk 1.1
@@ -30,6 +45,7 @@ Tue Jul 14 19:12:12 1998  Tim Janik  <timj@gtk.org>
        * gtk/gtkbutton.c (gtk_button_set_arg): don't destroy a buttons
        child if it isn't a label.
 
+>>>>>>> 1.520
 Tue Jul 14 11:41:38 1998  Tim Janik  <timj@gtk.org>
 
        * gtk/gtkdialog.h: 
index 753c024af7f38b873a33cf1127694eaa0f602a1e..43a7f540cebb0aa415dac7ff1ddce8365ec1f5e3 100644 (file)
@@ -1,3 +1,18 @@
+Thu Jul 16 01:10:02 1998  Lars Hamann  <lars@gtk.org>
+
+       * gtk/gtkctree.h :
+       * gtk/gtkctree.c :
+       Added focus handling, horizontal and vertical autoscrolling,
+       extended Selection Mode, key bindings
+       Added gtk_ctree_toggle_expansion & gtk_ctree_toggle_expansion_recursive
+       Changed gtk_ctree_scroll_to to gtk_ctree_moveto.
+       Removed gtk_ctree_clear.
+       
+       * gtk/gtkclist.h :
+       * gtk/gtkclist.c : 
+       Added focus handling, horizontal and vertical autoscrolling,
+       extended Selection Mode, key bindings
+
 Wed Jul 15 12:39:27 1998  Raph Levien  <raph@acm.org>
 
        * This checkin integrates GdkRgb version 0.0.3 into the Gtk 1.1
@@ -30,6 +45,7 @@ Tue Jul 14 19:12:12 1998  Tim Janik  <timj@gtk.org>
        * gtk/gtkbutton.c (gtk_button_set_arg): don't destroy a buttons
        child if it isn't a label.
 
+>>>>>>> 1.520
 Tue Jul 14 11:41:38 1998  Tim Janik  <timj@gtk.org>
 
        * gtk/gtkdialog.h: 
index 753c024af7f38b873a33cf1127694eaa0f602a1e..43a7f540cebb0aa415dac7ff1ddce8365ec1f5e3 100644 (file)
@@ -1,3 +1,18 @@
+Thu Jul 16 01:10:02 1998  Lars Hamann  <lars@gtk.org>
+
+       * gtk/gtkctree.h :
+       * gtk/gtkctree.c :
+       Added focus handling, horizontal and vertical autoscrolling,
+       extended Selection Mode, key bindings
+       Added gtk_ctree_toggle_expansion & gtk_ctree_toggle_expansion_recursive
+       Changed gtk_ctree_scroll_to to gtk_ctree_moveto.
+       Removed gtk_ctree_clear.
+       
+       * gtk/gtkclist.h :
+       * gtk/gtkclist.c : 
+       Added focus handling, horizontal and vertical autoscrolling,
+       extended Selection Mode, key bindings
+
 Wed Jul 15 12:39:27 1998  Raph Levien  <raph@acm.org>
 
        * This checkin integrates GdkRgb version 0.0.3 into the Gtk 1.1
@@ -30,6 +45,7 @@ Tue Jul 14 19:12:12 1998  Tim Janik  <timj@gtk.org>
        * gtk/gtkbutton.c (gtk_button_set_arg): don't destroy a buttons
        child if it isn't a label.
 
+>>>>>>> 1.520
 Tue Jul 14 11:41:38 1998  Tim Janik  <timj@gtk.org>
 
        * gtk/gtkdialog.h: 
index 753c024af7f38b873a33cf1127694eaa0f602a1e..43a7f540cebb0aa415dac7ff1ddce8365ec1f5e3 100644 (file)
@@ -1,3 +1,18 @@
+Thu Jul 16 01:10:02 1998  Lars Hamann  <lars@gtk.org>
+
+       * gtk/gtkctree.h :
+       * gtk/gtkctree.c :
+       Added focus handling, horizontal and vertical autoscrolling,
+       extended Selection Mode, key bindings
+       Added gtk_ctree_toggle_expansion & gtk_ctree_toggle_expansion_recursive
+       Changed gtk_ctree_scroll_to to gtk_ctree_moveto.
+       Removed gtk_ctree_clear.
+       
+       * gtk/gtkclist.h :
+       * gtk/gtkclist.c : 
+       Added focus handling, horizontal and vertical autoscrolling,
+       extended Selection Mode, key bindings
+
 Wed Jul 15 12:39:27 1998  Raph Levien  <raph@acm.org>
 
        * This checkin integrates GdkRgb version 0.0.3 into the Gtk 1.1
@@ -30,6 +45,7 @@ Tue Jul 14 19:12:12 1998  Tim Janik  <timj@gtk.org>
        * gtk/gtkbutton.c (gtk_button_set_arg): don't destroy a buttons
        child if it isn't a label.
 
+>>>>>>> 1.520
 Tue Jul 14 11:41:38 1998  Tim Janik  <timj@gtk.org>
 
        * gtk/gtkdialog.h: 
index 753c024af7f38b873a33cf1127694eaa0f602a1e..43a7f540cebb0aa415dac7ff1ddce8365ec1f5e3 100644 (file)
@@ -1,3 +1,18 @@
+Thu Jul 16 01:10:02 1998  Lars Hamann  <lars@gtk.org>
+
+       * gtk/gtkctree.h :
+       * gtk/gtkctree.c :
+       Added focus handling, horizontal and vertical autoscrolling,
+       extended Selection Mode, key bindings
+       Added gtk_ctree_toggle_expansion & gtk_ctree_toggle_expansion_recursive
+       Changed gtk_ctree_scroll_to to gtk_ctree_moveto.
+       Removed gtk_ctree_clear.
+       
+       * gtk/gtkclist.h :
+       * gtk/gtkclist.c : 
+       Added focus handling, horizontal and vertical autoscrolling,
+       extended Selection Mode, key bindings
+
 Wed Jul 15 12:39:27 1998  Raph Levien  <raph@acm.org>
 
        * This checkin integrates GdkRgb version 0.0.3 into the Gtk 1.1
@@ -30,6 +45,7 @@ Tue Jul 14 19:12:12 1998  Tim Janik  <timj@gtk.org>
        * gtk/gtkbutton.c (gtk_button_set_arg): don't destroy a buttons
        child if it isn't a label.
 
+>>>>>>> 1.520
 Tue Jul 14 11:41:38 1998  Tim Janik  <timj@gtk.org>
 
        * gtk/gtkdialog.h: 
index 753c024af7f38b873a33cf1127694eaa0f602a1e..43a7f540cebb0aa415dac7ff1ddce8365ec1f5e3 100644 (file)
@@ -1,3 +1,18 @@
+Thu Jul 16 01:10:02 1998  Lars Hamann  <lars@gtk.org>
+
+       * gtk/gtkctree.h :
+       * gtk/gtkctree.c :
+       Added focus handling, horizontal and vertical autoscrolling,
+       extended Selection Mode, key bindings
+       Added gtk_ctree_toggle_expansion & gtk_ctree_toggle_expansion_recursive
+       Changed gtk_ctree_scroll_to to gtk_ctree_moveto.
+       Removed gtk_ctree_clear.
+       
+       * gtk/gtkclist.h :
+       * gtk/gtkclist.c : 
+       Added focus handling, horizontal and vertical autoscrolling,
+       extended Selection Mode, key bindings
+
 Wed Jul 15 12:39:27 1998  Raph Levien  <raph@acm.org>
 
        * This checkin integrates GdkRgb version 0.0.3 into the Gtk 1.1
@@ -30,6 +45,7 @@ Tue Jul 14 19:12:12 1998  Tim Janik  <timj@gtk.org>
        * gtk/gtkbutton.c (gtk_button_set_arg): don't destroy a buttons
        child if it isn't a label.
 
+>>>>>>> 1.520
 Tue Jul 14 11:41:38 1998  Tim Janik  <timj@gtk.org>
 
        * gtk/gtkdialog.h: 
index 753c024af7f38b873a33cf1127694eaa0f602a1e..43a7f540cebb0aa415dac7ff1ddce8365ec1f5e3 100644 (file)
@@ -1,3 +1,18 @@
+Thu Jul 16 01:10:02 1998  Lars Hamann  <lars@gtk.org>
+
+       * gtk/gtkctree.h :
+       * gtk/gtkctree.c :
+       Added focus handling, horizontal and vertical autoscrolling,
+       extended Selection Mode, key bindings
+       Added gtk_ctree_toggle_expansion & gtk_ctree_toggle_expansion_recursive
+       Changed gtk_ctree_scroll_to to gtk_ctree_moveto.
+       Removed gtk_ctree_clear.
+       
+       * gtk/gtkclist.h :
+       * gtk/gtkclist.c : 
+       Added focus handling, horizontal and vertical autoscrolling,
+       extended Selection Mode, key bindings
+
 Wed Jul 15 12:39:27 1998  Raph Levien  <raph@acm.org>
 
        * This checkin integrates GdkRgb version 0.0.3 into the Gtk 1.1
@@ -30,6 +45,7 @@ Tue Jul 14 19:12:12 1998  Tim Janik  <timj@gtk.org>
        * gtk/gtkbutton.c (gtk_button_set_arg): don't destroy a buttons
        child if it isn't a label.
 
+>>>>>>> 1.520
 Tue Jul 14 11:41:38 1998  Tim Janik  <timj@gtk.org>
 
        * gtk/gtkdialog.h: 
diff --git a/TODO b/TODO
index 1e66fee7a0312c82018fec13fea30de32c963ada..7be80a4428848a1aebb6f356ca6472f8e9460ae1 100644 (file)
--- a/TODO
+++ b/TODO
@@ -5,14 +5,6 @@ For 1.2.0 release:
        non-functional ones.
        
 Bugs:
- * gtk_ctree_set_selection_mode should be a mere alias of
-        gtk_clist_set_selection_mode, and clist should implement a new member
-        function `set_selection_mode' (doesn't need to have a gtk_signal_new
-        declaration) that can be overridden by ctree, this
-        is needed for proper widget argument support on GtkCList basis.
-        this applies similarly to `gtk_ctree_clear', one should be able to
-        get the same effect by invoking gtk_clist_clear (GTK_CLIST (ctree));
-
  * Widget redrawing when the window resizes sometimes messes up.
    GtkLabels sometimes redraw without clearing up the underlying background on
    window resizes.
index 99674ee2c21cd50a16ee6f9d477f35fb9985749d..f8cbe3326d4a04f22ef731ed24594b26ce77cceb 100644 (file)
  * Boston, MA 02111-1307, USA.
  */
 #include <stdlib.h>
+#include <string.h>
 #include "../config.h"
+#include "gtkmain.h"
 #include "gtkclist.h"
+#include "gtkbindings.h"
+#include <gdk/gdkkeysyms.h>
 
 /* the number rows memchunk expands at a time */
 #define CLIST_OPTIMUM_SIZE 512
@@ -36,6 +40,9 @@
 /* added the horizontal space at the beginning and end of a row*/
 #define COLUMN_INSET 3
 
+/* used for auto-scrolling */
+#define SCROLL_TIME  100
+
 /* scrollbar spacing class macro */
 #define SCROLLBAR_SPACING(w) (GTK_CLIST_CLASS (GTK_OBJECT (w)->klass)->scrollbar_spacing)
 
@@ -52,7 +59,7 @@
 
 /* gives the left pixel of the given column in context of
  * the clist's hoffset */
-#define COLUMN_LEFT_XPIXEL(clist, column)  ((clist)->column[(column)].area.x + \
+#define COLUMN_LEFT_XPIXEL(clist, colnum)  ((clist)->column[(colnum)].area.x + \
                                            (clist)->hoffset)
 
 /* returns the column index from a x pixel location in the 
@@ -82,7 +89,7 @@ COLUMN_FROM_XPIXEL (GtkCList * clist,
 
 /* returns the left pixel of the given column in the context of
  * the list width */
-#define COLUMN_LEFT(clist, column) ((clist)->column[(column)].area.x)
+#define COLUMN_LEFT(clist, colnum) ((clist)->column[(colnum)].area.x)
 
 /* returns the total height of the list */
 #define LIST_HEIGHT(clist)         (((clist)->row_height * ((clist)->rows)) + \
@@ -94,12 +101,26 @@ COLUMN_FROM_XPIXEL (GtkCList * clist,
                                    COLUMN_INSET + CELL_SPACING)
 
 
+#define GTK_CLIST_CLASS_FW(_widget_) GTK_CLIST_CLASS (GTK_OBJECT (_widget_)->klass)
+
+
 /* Signals */
 enum
 {
   SELECT_ROW,
   UNSELECT_ROW,
   CLICK_COLUMN,
+  TOGGLE_FOCUS_ROW,
+  SELECT_ALL,
+  UNSELECT_ALL,
+  UNDO_SELECTION,
+  START_SELECTION,
+  END_SELECTION,
+  TOGGLE_ADD_MODE,
+  EXTEND_SELECTION,
+  SCROLL_VERTICAL,
+  SCROLL_HORIZONTAL,
+  ABORT_COLUMN_RESIZE,
   LAST_SIGNAL
 };
 
@@ -113,11 +134,23 @@ enum
 typedef void (*GtkCListSignal1) (GtkObject * object,
                                 gint arg1,
                                 gint arg2,
-                                GdkEventButton * arg3,
+                                GdkEvent * arg3,
                                 gpointer data);
 
-typedef void (*GtkCListSignal2) (GtkObject * object,
+typedef void (*GtkCListSignal2) (GtkObject *object,
+                                gpointer data); 
+
+typedef void (*GtkCListSignal3) (GtkObject * object,
+                                gint arg1,
+                                gpointer data);
+typedef void (*GtkCListSignal4) (GtkObject * object,
+                                gint arg1,
+                                gfloat arg2,
+                                gpointer data);
+typedef void (*GtkCListSignal5) (GtkObject * object,
                                 gint arg1,
+                                gfloat arg2,
+                                gboolean arg3,
                                 gpointer data);
 
 
@@ -128,6 +161,7 @@ static void sync_selection (GtkCList * clist,
 /* GtkCList Methods */
 static void gtk_clist_class_init (GtkCListClass * klass);
 static void gtk_clist_init (GtkCList * clist);
+static void real_clear (GtkCList * clist);
 
 /* GtkObject Methods */
 static void gtk_clist_destroy (GtkObject * object);
@@ -149,7 +183,6 @@ static gint gtk_clist_button_release (GtkWidget * widget,
                                      GdkEventButton * event);
 static gint gtk_clist_motion (GtkWidget * widget, 
                              GdkEventMotion * event);
-
 static void gtk_clist_size_request (GtkWidget * widget,
                                    GtkRequisition * requisition);
 static void gtk_clist_size_allocate (GtkWidget * widget,
@@ -181,24 +214,42 @@ static void size_allocate_columns (GtkCList * clist);
 static void toggle_row (GtkCList * clist,
                        gint row,
                        gint column,
-                       GdkEventButton * event);
+                       GdkEvent * event);
 static void select_row (GtkCList * clist,
                        gint row,
                        gint column,
-                       GdkEventButton * event);
+                       GdkEvent * event);
 static void unselect_row (GtkCList * clist,
                          gint row,
                          gint column,
-                         GdkEventButton * event);
-
+                         GdkEvent * event);
 static void real_select_row (GtkCList * clist,
                             gint row,
                             gint column,
-                            GdkEventButton * event);
+                            GdkEvent * event);
 static void real_unselect_row (GtkCList * clist,
                               gint row,
                               gint column,
-                              GdkEventButton * event);
+                              GdkEvent * event);
+static void update_extended_selection (GtkCList *clist,
+                                      gint row);
+static GList * selection_find (GtkCList *clist,
+                              gint row_number,
+                              GList *row_list_element);
+static void real_select_all (GtkCList * clist);
+static void real_unselect_all (GtkCList * clist);
+static void move_vertical (GtkCList *clist,
+                          gint row,
+                          gfloat align);
+static void move_horizontal (GtkCList *clist,
+                            gint diff);
+static void real_undo_selection (GtkCList * clist);
+static void fake_unselect_all (GtkCList *clist,
+                              gint row);
+static void fake_toggle_row (GtkCList *clist,
+                            gint row);
+static void resync_selection (GtkCList *clist,
+                             GdkEvent *event);
 
 /* Resize Columns */
 static void draw_xor_line (GtkCList * clist);
@@ -209,6 +260,7 @@ static gint new_column_width (GtkCList * clist,
 static void resize_column (GtkCList * clist,
                           gint column,
                           gint width);
+static void abort_column_resize (GtkCList *clist);
 
 /* Buttons */
 static void column_button_create (GtkCList * clist,
@@ -229,7 +281,6 @@ static void hadjustment_changed (GtkAdjustment * adjustment,
 static void hadjustment_value_changed (GtkAdjustment * adjustment,
                                       gpointer data);
 
-
 /* Memory Allocation/Distruction Routines */
 static GtkCListColumn *columns_new (GtkCList * clist);
 
@@ -262,6 +313,44 @@ static void cell_set_pixtext (GtkCList * clist,
                              GdkPixmap * pixmap,
                              GdkBitmap * mask);
 
+/* Focus handling */
+static void gtk_clist_draw_focus (GtkWidget *widget);
+static gint gtk_clist_focus_in   (GtkWidget     *widget,
+                                 GdkEventFocus *event);
+static gint gtk_clist_focus_out  (GtkWidget     *widget,
+                                 GdkEventFocus *event);
+static gint gtk_clist_focus      (GtkContainer     *container,
+                                 GtkDirectionType  direction);
+static void gtk_clist_set_focus_child (GtkContainer *container,
+                                      GtkWidget    *child);
+static gint gtk_clist_key_press  (GtkWidget   *widget,
+                                 GdkEventKey *event);
+
+/* Selection handling */
+static void set_anchor (GtkCList *clist,
+                       gboolean  add_mode,
+                       gint      anchor,
+                       gint      undo_anchor);
+static void start_selection (GtkCList *clist);
+static void end_selection (GtkCList *clist);
+
+static void toggle_add_mode (GtkCList *clist);
+static void toggle_focus_row (GtkCList *clist);
+static void move_focus_row (GtkCList      *clist,
+                           GtkScrollType  scroll_type,
+                           gfloat         position);
+static void scroll_horizontal (GtkCList      *clist,
+                              GtkScrollType  scroll_type,
+                              gfloat         position);
+static void scroll_vertical (GtkCList      *clist,
+                            GtkScrollType  scroll_type,
+                            gfloat         position);
+static void extend_selection (GtkCList      *clist,
+                             GtkScrollType  scroll_type,
+                             gfloat         position,
+                             gboolean       auto_start_selection);
+
+
 /* Signals */
 static void gtk_clist_marshal_signal_1 (GtkObject * object,
                                        GtkSignalFunc func,
@@ -271,6 +360,20 @@ static void gtk_clist_marshal_signal_2 (GtkObject * object,
                                        GtkSignalFunc func,
                                        gpointer func_data,
                                        GtkArg * args);
+static void gtk_clist_marshal_signal_3 (GtkObject * object,
+                                       GtkSignalFunc func,
+                                       gpointer func_data,
+                                       GtkArg * args);
+static void gtk_clist_marshal_signal_4 (GtkObject * object,
+                                       GtkSignalFunc func,
+                                       gpointer func_data,
+                                       GtkArg * args);
+static void gtk_clist_marshal_signal_5 (GtkObject * object,
+                                       GtkSignalFunc func,
+                                       gpointer func_data,
+                                       GtkArg * args);
+
+
 
 /* Fill in data after widget is realized and has style */
 
@@ -295,8 +398,8 @@ gtk_clist_get_type (void)
        (GtkClassInitFunc) gtk_clist_class_init,
        (GtkObjectInitFunc) gtk_clist_init,
        /* reserved_1 */ NULL,
-        /* reserved_2 */ NULL,
-        (GtkClassInitFunc) NULL,
+       /* reserved_2 */ NULL,
+       (GtkClassInitFunc) NULL,
       };
 
       clist_type = gtk_type_unique (gtk_container_get_type (), &clist_info);
@@ -337,9 +440,89 @@ gtk_clist_class_init (GtkCListClass * klass)
                    GTK_RUN_FIRST,
                    object_class->type,
                    GTK_SIGNAL_OFFSET (GtkCListClass, click_column),
-                   gtk_clist_marshal_signal_2,
+                   gtk_clist_marshal_signal_3,
                    GTK_TYPE_NONE, 1, GTK_TYPE_INT);
 
+  clist_signals[TOGGLE_FOCUS_ROW] =
+    gtk_signal_new ("toggle_focus_row",
+                    GTK_RUN_LAST | GTK_RUN_ACTION,
+                    object_class->type,
+                    GTK_SIGNAL_OFFSET (GtkCListClass, toggle_focus_row),
+                    gtk_clist_marshal_signal_2,
+                    GTK_TYPE_NONE, 0);
+  clist_signals[SELECT_ALL] =
+    gtk_signal_new ("select_all",
+                    GTK_RUN_LAST | GTK_RUN_ACTION,
+                    object_class->type,
+                    GTK_SIGNAL_OFFSET (GtkCListClass, select_all),
+                    gtk_clist_marshal_signal_2,
+                    GTK_TYPE_NONE, 0);
+  clist_signals[UNSELECT_ALL] =
+    gtk_signal_new ("unselect_all",
+                    GTK_RUN_LAST | GTK_RUN_ACTION,
+                    object_class->type,
+                    GTK_SIGNAL_OFFSET (GtkCListClass, unselect_all),
+                    gtk_clist_marshal_signal_2,
+                    GTK_TYPE_NONE, 0);
+  clist_signals[UNDO_SELECTION] =
+    gtk_signal_new ("undo_selection",
+                   GTK_RUN_LAST | GTK_RUN_ACTION,
+                   object_class->type,
+                   GTK_SIGNAL_OFFSET (GtkCListClass, undo_selection),
+                   gtk_clist_marshal_signal_2,
+                   GTK_TYPE_NONE, 0);
+  clist_signals[START_SELECTION] =
+    gtk_signal_new ("start_selection",
+                   GTK_RUN_LAST | GTK_RUN_ACTION,
+                   object_class->type,
+                   GTK_SIGNAL_OFFSET (GtkCListClass, start_selection),
+                   gtk_clist_marshal_signal_2,
+                   GTK_TYPE_NONE, 0);
+  clist_signals[END_SELECTION] =
+    gtk_signal_new ("end_selection",
+                   GTK_RUN_LAST | GTK_RUN_ACTION,
+                   object_class->type,
+                   GTK_SIGNAL_OFFSET (GtkCListClass, end_selection),
+                   gtk_clist_marshal_signal_2,
+                   GTK_TYPE_NONE, 0);
+  clist_signals[TOGGLE_ADD_MODE] =
+    gtk_signal_new ("toggle_add_mode",
+                   GTK_RUN_LAST | GTK_RUN_ACTION,
+                   object_class->type,
+                   GTK_SIGNAL_OFFSET (GtkCListClass, toggle_add_mode),
+                   gtk_clist_marshal_signal_2,
+                   GTK_TYPE_NONE, 0);
+  clist_signals[EXTEND_SELECTION] =
+    gtk_signal_new ("extend_selection",
+                    GTK_RUN_LAST | GTK_RUN_ACTION,
+                    object_class->type,
+                    GTK_SIGNAL_OFFSET (GtkCListClass, extend_selection),
+                    gtk_clist_marshal_signal_5,
+                    GTK_TYPE_NONE, 3,
+                   GTK_TYPE_ENUM, GTK_TYPE_FLOAT, GTK_TYPE_BOOL);
+  clist_signals[SCROLL_VERTICAL] =
+    gtk_signal_new ("scroll_vertical",
+                    GTK_RUN_LAST | GTK_RUN_ACTION,
+                    object_class->type,
+                    GTK_SIGNAL_OFFSET (GtkCListClass, scroll_vertical),
+                    gtk_clist_marshal_signal_4,
+                    GTK_TYPE_NONE, 2, GTK_TYPE_ENUM, GTK_TYPE_FLOAT);
+  clist_signals[SCROLL_HORIZONTAL] =
+    gtk_signal_new ("scroll_horizontal",
+                    GTK_RUN_LAST | GTK_RUN_ACTION,
+                    object_class->type,
+                    GTK_SIGNAL_OFFSET (GtkCListClass, scroll_horizontal),
+                    gtk_clist_marshal_signal_4,
+                    GTK_TYPE_NONE, 2, GTK_TYPE_ENUM, GTK_TYPE_FLOAT);
+  clist_signals[ABORT_COLUMN_RESIZE] =
+    gtk_signal_new ("abort_column_resize",
+                    GTK_RUN_LAST | GTK_RUN_ACTION,
+                    object_class->type,
+                    GTK_SIGNAL_OFFSET (GtkCListClass, abort_column_resize),
+                    gtk_clist_marshal_signal_2,
+                    GTK_TYPE_NONE, 0);
+
+
   gtk_object_class_add_signals (object_class, clist_signals, LAST_SIGNAL);
 
   object_class->destroy = gtk_clist_destroy;
@@ -356,18 +539,230 @@ gtk_clist_class_init (GtkCListClass * klass)
   widget_class->expose_event = gtk_clist_expose;
   widget_class->size_request = gtk_clist_size_request;
   widget_class->size_allocate = gtk_clist_size_allocate;
+  widget_class->key_press_event = gtk_clist_key_press;
+  widget_class->focus_in_event = gtk_clist_focus_in;
+  widget_class->focus_out_event = gtk_clist_focus_out;
+  widget_class->draw_focus = gtk_clist_draw_focus;
 
   /* container_class->add = NULL; use the default GtkContainerClass warning */
   /* container_class->remove = NULL; use the default GtkContainerClass warning */
   container_class->foreach = gtk_clist_foreach;
+  container_class->focus = gtk_clist_focus;
+  container_class->set_focus_child = gtk_clist_set_focus_child;
 
   klass->select_row = real_select_row;
   klass->unselect_row = real_unselect_row;
+  klass->undo_selection = real_undo_selection;
+  klass->resync_selection = resync_selection;
+  klass->selection_find = selection_find;
   klass->click_column = NULL;
-
   klass->draw_row = draw_row;
+  klass->clear = real_clear;
+  klass->select_all = real_select_all;
+  klass->unselect_all = real_unselect_all;
+  klass->fake_unselect_all = fake_unselect_all;
+  klass->scroll_horizontal = scroll_horizontal;
+  klass->scroll_vertical = scroll_vertical;
+  klass->extend_selection = extend_selection;
+  klass->toggle_focus_row = toggle_focus_row;
+  klass->toggle_add_mode = toggle_add_mode;
+  klass->start_selection = start_selection;
+  klass->end_selection = end_selection;
+  klass->abort_column_resize = abort_column_resize;
 
   klass->scrollbar_spacing = 5;
+
+  {
+    GtkBindingSet *binding_set;
+
+    binding_set = gtk_binding_set_by_class (klass);
+    gtk_binding_entry_add_signal (binding_set, GDK_Up, 0,
+                                 "scroll_vertical", 2,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
+                                  GTK_TYPE_FLOAT, 0.0);
+    gtk_binding_entry_add_signal (binding_set, GDK_Down, 0,
+                                 "scroll_vertical", 2,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
+                                  GTK_TYPE_FLOAT, 0.0);
+    gtk_binding_entry_add_signal (binding_set, GDK_Page_Up, 0,
+                                 "scroll_vertical", 2,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_PAGE_BACKWARD,
+                                  GTK_TYPE_FLOAT, 0.0);
+    gtk_binding_entry_add_signal (binding_set, GDK_Page_Down, 0,
+                                 "scroll_vertical", 2,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_PAGE_FORWARD,
+                                  GTK_TYPE_FLOAT, 0.0);
+    gtk_binding_entry_add_signal (binding_set, GDK_Home, GDK_CONTROL_MASK,
+                                 "scroll_vertical", 2,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
+                                  GTK_TYPE_FLOAT, 0.0);
+    gtk_binding_entry_add_signal (binding_set, GDK_End, GDK_CONTROL_MASK,
+                                 "scroll_vertical", 2,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
+                                  GTK_TYPE_FLOAT, 1.0);
+
+    gtk_binding_entry_add_signal (binding_set, GDK_Up, GDK_SHIFT_MASK,
+                                 "extend_selection", 3,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
+                                  GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
+    gtk_binding_entry_add_signal (binding_set, GDK_Down, GDK_SHIFT_MASK,
+                                 "extend_selection", 3,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
+                                  GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
+    gtk_binding_entry_add_signal (binding_set, GDK_Page_Up, GDK_SHIFT_MASK,
+                                 "extend_selection", 3,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_PAGE_BACKWARD,
+                                  GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
+    gtk_binding_entry_add_signal (binding_set, GDK_Page_Down, GDK_SHIFT_MASK,
+                                 "extend_selection", 3,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_PAGE_FORWARD,
+                                  GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
+    gtk_binding_entry_add_signal (binding_set, GDK_Home,
+                                 GDK_SHIFT_MASK | GDK_CONTROL_MASK,
+                                 "extend_selection", 3,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
+                                  GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
+    gtk_binding_entry_add_signal (binding_set, GDK_End,
+                                 GDK_SHIFT_MASK | GDK_CONTROL_MASK,
+                                 "extend_selection", 3,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
+                                  GTK_TYPE_FLOAT, 1.0, GTK_TYPE_BOOL, TRUE);
+
+
+    gtk_binding_entry_add_signal (binding_set, GDK_Left, 0,
+                                 "scroll_horizontal", 2,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
+                                  GTK_TYPE_FLOAT, 0.0);
+    gtk_binding_entry_add_signal (binding_set, GDK_Right, 0,
+                                 "scroll_horizontal", 2,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
+                                  GTK_TYPE_FLOAT, 0.0);
+    gtk_binding_entry_add_signal (binding_set, GDK_Home, 0,
+                                 "scroll_horizontal", 2,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
+                                  GTK_TYPE_FLOAT, 0.0);
+    gtk_binding_entry_add_signal (binding_set, GDK_End, 0,
+                                 "scroll_horizontal", 2,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
+                                  GTK_TYPE_FLOAT, 1.0);
+
+
+    gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0,
+                                 "undo_selection", 0);
+    gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0,
+                                 "abort_column_resize", 0);
+    gtk_binding_entry_add_signal (binding_set, GDK_space, 0,
+                                 "toggle_focus_row", 0);
+    gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_CONTROL_MASK,
+                                 "toggle_add_mode", 0);
+    gtk_binding_entry_add_signal (binding_set, '/', GDK_CONTROL_MASK,
+                                 "select_all", 0);
+    gtk_binding_entry_add_signal (binding_set, '\\', GDK_CONTROL_MASK,
+                                 "unselect_all", 0);
+    gtk_binding_entry_add_signal (binding_set, GDK_Shift_L,
+                                 GDK_RELEASE_MASK | GDK_SHIFT_MASK,
+                                 "end_selection", 0);
+    gtk_binding_entry_add_signal (binding_set, GDK_Shift_R,
+                                 GDK_RELEASE_MASK | GDK_SHIFT_MASK,
+                                 "end_selection", 0);
+  }
+
+  /*
+  {
+    GtkBindingSet *binding_set;
+
+    binding_set = gtk_binding_set_by_class (klass);
+    gtk_binding_entry_add_signal (binding_set, GDK_Up, 0,
+                                 "scroll_vertical", 2,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
+                                  GTK_TYPE_FLOAT, 0.0);
+    gtk_binding_entry_add_signal (binding_set, GDK_Down, 0,
+                                 "scroll_vertical", 2,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
+                                  GTK_TYPE_FLOAT, 0.0);
+    gtk_binding_entry_add_signal (binding_set, GDK_Page_Up, 0,
+                                 "scroll_vertical", 2,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_PAGE_BACKWARD,
+                                  GTK_TYPE_FLOAT, 0.0);
+    gtk_binding_entry_add_signal (binding_set, GDK_Page_Down, 0,
+                                 "scroll_vertical", 2,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_PAGE_FORWARD,
+                                  GTK_TYPE_FLOAT, 0.0);
+    gtk_binding_entry_add_signal (binding_set, GDK_Home, GDK_CONTROL_MASK,
+                                 "scroll_vertical", 2,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
+                                  GTK_TYPE_FLOAT, 0.0);
+    gtk_binding_entry_add_signal (binding_set, GDK_End, GDK_CONTROL_MASK,
+                                 "scroll_vertical", 2,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
+                                  GTK_TYPE_FLOAT, 0);
+
+    gtk_binding_entry_add_signal (binding_set, GDK_KP_Up, 0,
+                                 "extend_selection", 3,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
+                                  GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
+    gtk_binding_entry_add_signal (binding_set, GDK_KP_Down, 0,
+                                 "extend_selection", 3,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
+                                  GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
+    gtk_binding_entry_add_signal (binding_set, GDK_KP_Page_Up, 0,
+                                 "extend_selection", 3,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_PAGE_BACKWARD,
+                                  GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
+    gtk_binding_entry_add_signal (binding_set, GDK_KP_Page_Down, 0,
+                                 "extend_selection", 3,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_PAGE_FORWARD,
+                                  GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
+    gtk_binding_entry_add_signal (binding_set, GDK_KP_Home, 0,
+                                 "extend_selection", 3,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
+                                  GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
+    gtk_binding_entry_add_signal (binding_set, GDK_KP_End, 0,
+                                 "extend_selection", 3,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
+                                  GTK_TYPE_FLOAT, 1.0, GTK_TYPE_BOOL, TRUE);
+
+    gtk_binding_entry_add_signal (binding_set, GDK_Left, 0,
+                                 "scroll_horizontal", 2,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
+                                  GTK_TYPE_FLOAT, 0.0);
+    gtk_binding_entry_add_signal (binding_set, GDK_KP_Left, 0,
+                                 "scroll_horizontal", 2,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
+                                  GTK_TYPE_FLOAT, 0.0);
+    gtk_binding_entry_add_signal (binding_set, GDK_Right, 0,
+                                 "scroll_horizontal", 2,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
+                                  GTK_TYPE_FLOAT, 0.0);
+    gtk_binding_entry_add_signal (binding_set, GDK_KP_Right, 0,
+                                 "scroll_horizontal", 2,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
+                                  GTK_TYPE_FLOAT, 0.0);
+    gtk_binding_entry_add_signal (binding_set, GDK_Home, 0,
+                                 "scroll_horizontal", 2,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
+                                  GTK_TYPE_FLOAT, 0.0);
+    gtk_binding_entry_add_signal (binding_set, GDK_End, 0,
+                                 "sroll_horizontal", 2,
+                                 GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
+                                  GTK_TYPE_FLOAT, 1.0);
+
+    gtk_binding_entry_add_signal (binding_set, GDK_KP_Divide, 0,
+                                 "undo_selection", 0);
+    gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0,
+                                 "abort_column_resize", 0);
+    gtk_binding_entry_add_signal (binding_set, GDK_space, 0,
+                                 "toggle_focus_row", 0);
+    gtk_binding_entry_add_signal (binding_set, GDK_KP_Multiply, 0,
+                                 "toggle_add_mode", 0);
+    gtk_binding_entry_add_signal (binding_set, GDK_KP_Add, 0,
+                                 "select_all", 0);
+    gtk_binding_entry_add_signal (binding_set, GDK_KP_Subtract, 0,
+                                 "unselect_all", 0);
+    gtk_binding_entry_add_signal (binding_set, GDK_KP_Enter, 0,
+                                 "end_selection", 0);
+  }
+  */
 }
 
 static void
@@ -396,8 +791,48 @@ gtk_clist_marshal_signal_2 (GtkObject * object,
 
   rfunc = (GtkCListSignal2) func;
 
+  (*rfunc) (object, func_data);
+}
+
+static void
+gtk_clist_marshal_signal_3 (GtkObject * object,
+                           GtkSignalFunc func,
+                           gpointer func_data,
+                           GtkArg * args)
+{
+  GtkCListSignal3 rfunc;
+
+  rfunc = (GtkCListSignal3) func;
+
+  (*rfunc) (object, GTK_VALUE_INT (args[0]), func_data);
+}
+
+static void
+gtk_clist_marshal_signal_4 (GtkObject * object,
+                           GtkSignalFunc func,
+                           gpointer func_data,
+                           GtkArg * args)
+{
+  GtkCListSignal4 rfunc;
+
+  rfunc = (GtkCListSignal4) func;
+
   (*rfunc) (object, GTK_VALUE_INT (args[0]),
-           func_data);
+           GTK_VALUE_FLOAT (args[1]), func_data);
+}
+
+static void
+gtk_clist_marshal_signal_5 (GtkObject * object,
+                           GtkSignalFunc func,
+                           gpointer func_data,
+                           GtkArg * args)
+{
+  GtkCListSignal5 rfunc;
+
+  rfunc = (GtkCListSignal5) func;
+
+  (*rfunc) (object, GTK_VALUE_INT (args[0]), GTK_VALUE_FLOAT (args[1]),
+           GTK_VALUE_BOOL (args[2]), func_data);
 }
 
 static void
@@ -444,6 +879,20 @@ gtk_clist_init (GtkCList * clist)
 
   clist->selection_mode = GTK_SELECTION_SINGLE;
   clist->selection = NULL;
+  clist->selection_end = NULL;
+  clist->undo_selection = NULL;
+  clist->undo_unselection = NULL;
+
+  GTK_WIDGET_SET_FLAGS (clist, GTK_CAN_FOCUS);
+  GTK_CLIST_SET_FLAG (clist, CLIST_CHILD_HAS_FOCUS);
+  clist->focus_row = -1;
+  clist->undo_anchor = -1;
+
+  clist->anchor = -1;
+  clist->anchor_state = GTK_STATE_SELECTED;
+  clist->drag_pos = -1;
+  clist->htimer = 0;
+  clist->vtimer = 0;
 }
 
 /* Constructors */
@@ -537,6 +986,7 @@ gtk_clist_set_border (GtkCList * clist,
                      GtkShadowType border)
 {
   g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
 
   clist->shadow_type = border;
 
@@ -549,14 +999,39 @@ gtk_clist_set_selection_mode (GtkCList * clist,
                              GtkSelectionMode mode)
 {
   g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
+
+  if (mode == clist->selection_mode)
+    return;
 
   clist->selection_mode = mode;
+  clist->anchor = -1;
+  clist->anchor_state = GTK_STATE_SELECTED;
+  clist->drag_pos = -1;
+  clist->undo_anchor = clist->focus_row;
+
+  g_list_free (clist->undo_selection);
+  g_list_free (clist->undo_unselection);
+  clist->undo_selection = NULL;
+  clist->undo_unselection = NULL;
+
+  switch (mode)
+    {
+    case GTK_SELECTION_MULTIPLE:
+    case GTK_SELECTION_EXTENDED:
+      return;
+    case GTK_SELECTION_BROWSE:
+    case GTK_SELECTION_SINGLE:
+      gtk_clist_unselect_all (clist);
+      break;
+    }
 }
 
 void
 gtk_clist_freeze (GtkCList * clist)
 {
   g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
 
   GTK_CLIST_SET_FLAG (clist, CLIST_FROZEN);
 }
@@ -565,6 +1040,7 @@ void
 gtk_clist_thaw (GtkCList * clist)
 {
   g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
 
   GTK_CLIST_UNSET_FLAG (clist, CLIST_FROZEN);
 
@@ -576,6 +1052,7 @@ void
 gtk_clist_column_titles_show (GtkCList * clist)
 {
   g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
 
   if (!GTK_CLIST_SHOW_TITLES (clist))
     {
@@ -590,6 +1067,7 @@ void
 gtk_clist_column_titles_hide (GtkCList * clist)
 {
   g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
 
   if (GTK_CLIST_SHOW_TITLES (clist))
     {
@@ -605,6 +1083,7 @@ gtk_clist_column_title_active (GtkCList * clist,
                               gint column)
 {
   g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
 
   if (column < 0 || column >= clist->columns)
     return;
@@ -623,6 +1102,7 @@ gtk_clist_column_title_passive (GtkCList * clist,
                                gint column)
 {
   g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
 
   if (column < 0 || column >= clist->columns)
     return;
@@ -642,6 +1122,7 @@ gtk_clist_column_titles_active (GtkCList * clist)
   gint i;
 
   g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
 
   for (i = 0; i < clist->columns; i++)
     if (clist->column[i].button)
@@ -654,6 +1135,7 @@ gtk_clist_column_titles_passive (GtkCList * clist)
   gint i;
 
   g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
 
   for (i = 0; i < clist->columns; i++)
     if (clist->column[i].button)
@@ -671,6 +1153,7 @@ gtk_clist_set_column_title (GtkCList * clist,
   GtkWidget *label;
 
   g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
 
   if (column < 0 || column >= clist->columns)
     return;
@@ -731,6 +1214,7 @@ gtk_clist_set_column_widget (GtkCList * clist,
   GtkWidget *old_widget;
 
   g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
 
   if (column < 0 || column >= clist->columns)
     return;
@@ -771,6 +1255,7 @@ gtk_clist_set_column_justification (GtkCList * clist,
   GtkWidget *alignment;
 
   g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
 
   if (column < 0 || column >= clist->columns)
     return;
@@ -816,6 +1301,7 @@ gtk_clist_set_column_width (GtkCList * clist,
                            gint width)
 {
   g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
 
   if (column < 0 || column >= clist->columns)
     return;
@@ -844,6 +1330,7 @@ gtk_clist_set_row_height (GtkCList * clist,
   gint text_height;
 
   g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
 
   if (height > 0)
     clist->row_height = height;
@@ -873,51 +1360,40 @@ gtk_clist_moveto (GtkCList * clist,
                  gfloat row_align,
                  gfloat col_align)
 {
-  gint x, y;
-
   g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
 
   if (row < -1 || row >= clist->rows)
     return;
   if (column < -1 || column >= clist->columns)
     return;
 
-  /* adjust vertical scrollbar */
-  if (row >= 0)
-    {
-      x = ROW_TOP (clist, row) - (row_align * (clist->clist_window_height - 
-                                              (clist->row_height + 2 * CELL_SPACING)));
-      
-      if (x < 0)
-       GTK_RANGE (clist->vscrollbar)->adjustment->value = 0.0;
-      else if (x > LIST_HEIGHT (clist) - clist->clist_window_height)
-       GTK_RANGE (clist->vscrollbar)->adjustment->value = LIST_HEIGHT (clist) - 
-         clist->clist_window_height;
-      else
-       GTK_RANGE (clist->vscrollbar)->adjustment->value = x;
-      
-      gtk_signal_emit_by_name (GTK_OBJECT (GTK_RANGE (clist->vscrollbar)->adjustment), 
-                              "value_changed");
-    } 
-     
+  row_align = CLAMP (row_align, 0, 1);
+  col_align = CLAMP (col_align, 0, 1);
+
   /* adjust horizontal scrollbar */
   if (column >= 0)
     {
-      y = COLUMN_LEFT (clist, column) - (col_align * (clist->clist_window_width - 
-                                                     clist->column[column].area.width + 
-                                                     2 * (CELL_SPACING + COLUMN_INSET)));
-      
-      if (y < 0)
-       GTK_RANGE (clist->hscrollbar)->adjustment->value = 0.0;
-      else if (y > LIST_WIDTH (clist) - clist->clist_window_width)
-       GTK_RANGE (clist->hscrollbar)->adjustment->value = LIST_WIDTH (clist) - 
-         clist->clist_window_width;
+      GtkAdjustment *adj;
+      gint x;
+
+      adj = GTK_RANGE (clist->hscrollbar)->adjustment;
+
+      x = COLUMN_LEFT (clist, column) - CELL_SPACING - COLUMN_INSET -
+       (col_align * (clist->clist_window_width - 2 * COLUMN_INSET -
+                     CELL_SPACING - clist->column[column].area.width));
+      if (x < 0)
+       gtk_adjustment_set_value (adj, 0.0);
+      else if (x > LIST_WIDTH (clist) - clist->clist_window_width)
+       gtk_adjustment_set_value 
+         (adj, LIST_WIDTH (clist) - clist->clist_window_width);
       else
-       GTK_RANGE (clist->hscrollbar)->adjustment->value = y;
-      
-      gtk_signal_emit_by_name (GTK_OBJECT (GTK_RANGE (clist->hscrollbar)->adjustment), 
-                              "value_changed");
+       gtk_adjustment_set_value (adj, x);
     }
+
+  /* adjust vertical scrollbar */
+  if (row >= 0)
+    move_vertical (clist, row, row_align);
 }
 
 GtkCellType 
@@ -928,6 +1404,7 @@ gtk_clist_get_cell_type (GtkCList * clist,
   GtkCListRow *clist_row;
 
   g_return_val_if_fail (clist != NULL, -1);
+  g_return_val_if_fail (GTK_IS_CLIST (clist), -1);
 
   if (row < 0 || row >= clist->rows)
     return -1;
@@ -948,6 +1425,7 @@ gtk_clist_set_text (GtkCList * clist,
   GtkCListRow *clist_row;
 
   g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
 
   if (row < 0 || row >= clist->rows)
     return;
@@ -966,8 +1444,7 @@ gtk_clist_set_text (GtkCList * clist,
   if (!GTK_CLIST_FROZEN (clist))
     {
       if (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
-       (GTK_CLIST_CLASS (GTK_OBJECT (clist)->klass)->draw_row)
-         (clist, NULL, row, clist_row);
+       GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row, clist_row);
     }
 }
 
@@ -980,6 +1457,7 @@ gtk_clist_get_text (GtkCList * clist,
   GtkCListRow *clist_row;
 
   g_return_val_if_fail (clist != NULL, 0);
+  g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
 
   if (row < 0 || row >= clist->rows)
     return 0;
@@ -1007,6 +1485,7 @@ gtk_clist_set_pixmap (GtkCList * clist,
   GtkCListRow *clist_row;
 
   g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
 
   if (row < 0 || row >= clist->rows)
     return;
@@ -1025,8 +1504,7 @@ gtk_clist_set_pixmap (GtkCList * clist,
   if (!GTK_CLIST_FROZEN (clist))
     {
       if (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
-       (GTK_CLIST_CLASS (GTK_OBJECT (clist)->klass)->draw_row)
-         (clist, NULL, row, clist_row);
+       GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row, clist_row);
     }
 }
 
@@ -1040,6 +1518,7 @@ gtk_clist_get_pixmap (GtkCList * clist,
   GtkCListRow *clist_row;
 
   g_return_val_if_fail (clist != NULL, 0);
+  g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
 
   if (row < 0 || row >= clist->rows)
     return 0;
@@ -1073,6 +1552,7 @@ gtk_clist_set_pixtext (GtkCList * clist,
   GtkCListRow *clist_row;
 
   g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
 
   if (row < 0 || row >= clist->rows)
     return;
@@ -1089,8 +1569,7 @@ gtk_clist_set_pixtext (GtkCList * clist,
   if (!GTK_CLIST_FROZEN (clist))
     {
       if (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
-       (GTK_CLIST_CLASS (GTK_OBJECT (clist)->klass)->draw_row) 
-         (clist, NULL, row, clist_row);
+       GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row, clist_row);
     }
 }
 
@@ -1106,6 +1585,7 @@ gtk_clist_get_pixtext (GtkCList * clist,
   GtkCListRow *clist_row;
 
   g_return_val_if_fail (clist != NULL, 0);
+  g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
 
   if (row < 0 || row >= clist->rows)
     return 0;
@@ -1138,6 +1618,7 @@ gtk_clist_set_foreground (GtkCList * clist,
   GtkCListRow *clist_row;
 
   g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
 
   if (row < 0 || row >= clist->rows)
     return;
@@ -1154,8 +1635,7 @@ gtk_clist_set_foreground (GtkCList * clist,
 
   if (!GTK_CLIST_FROZEN (clist)
       && (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
-    (GTK_CLIST_CLASS (GTK_OBJECT (clist)->klass)->draw_row)
-      (clist, NULL, row, clist_row);
+    GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row, clist_row);
 }
 
 void
@@ -1166,6 +1646,7 @@ gtk_clist_set_background (GtkCList * clist,
   GtkCListRow *clist_row;
 
   g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
 
   if (row < 0 || row >= clist->rows)
     return;
@@ -1182,8 +1663,7 @@ gtk_clist_set_background (GtkCList * clist,
 
   if (!GTK_CLIST_FROZEN (clist)
       && (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
-    (GTK_CLIST_CLASS (GTK_OBJECT (clist)->klass)->draw_row)
-      (clist, NULL, row, clist_row);
+    GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row, clist_row);
 }
 
 void
@@ -1196,6 +1676,7 @@ gtk_clist_set_shift (GtkCList * clist,
   GtkCListRow *clist_row;
 
   g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
 
   if (row < 0 || row >= clist->rows)
     return;
@@ -1209,8 +1690,7 @@ gtk_clist_set_shift (GtkCList * clist,
 
   if (!GTK_CLIST_FROZEN (clist)
       && (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
-    (GTK_CLIST_CLASS (GTK_OBJECT (clist)->klass)->draw_row) 
-      (clist, NULL, row, clist_row);
+    GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row, clist_row);
 }
 
 gint
@@ -1221,6 +1701,7 @@ gtk_clist_append (GtkCList * clist,
   GtkCListRow *clist_row;
 
   g_return_val_if_fail (clist != NULL, -1);
+  g_return_val_if_fail (GTK_IS_CLIST (clist), -1);
 
   clist_row = row_new (clist);
   clist->rows++;
@@ -1243,7 +1724,7 @@ gtk_clist_append (GtkCList * clist,
       switch (clist->selection_mode)
        {
        case GTK_SELECTION_BROWSE:
-         gtk_clist_select_row (clist, 0, -1);
+         select_row (clist, 0, -1, NULL);
          break;
 
        default:
@@ -1275,6 +1756,7 @@ gtk_clist_insert (GtkCList * clist,
   GtkCListRow *clist_row;
 
   g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
   g_return_if_fail (text != NULL);
 
   /* return if out of bounds */
@@ -1303,6 +1785,9 @@ gtk_clist_insert (GtkCList * clist,
 
       clist->rows++;
 
+      if (row < ROW_FROM_YPIXEL (clist, 0))
+       clist->voffset -= (clist->row_height + CELL_SPACING);
+
       /* syncronize the selection list */
       sync_selection (clist, row, SYNC_INSERT);
     }
@@ -1326,6 +1811,7 @@ gtk_clist_remove (GtkCList * clist,
   GtkCListRow *clist_row;
 
   g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
 
   /* return if out of bounds */
   if (row < 0 || row > (clist->rows - 1))
@@ -1344,14 +1830,16 @@ gtk_clist_remove (GtkCList * clist,
    * removal */
   if (clist_row->state == GTK_STATE_SELECTED)
     {
-      was_selected = 1;
-
       switch (clist->selection_mode)
        {
        case GTK_SELECTION_SINGLE:
-       case GTK_SELECTION_BROWSE:
        case GTK_SELECTION_MULTIPLE:
-         gtk_clist_unselect_row (clist, row, -1);
+       case GTK_SELECTION_EXTENDED:
+         unselect_row (clist, row, -1, NULL);
+         break;
+
+       case GTK_SELECTION_BROWSE:
+         select_row (clist, row - 1, -1, NULL);
          break;
 
        default:
@@ -1363,27 +1851,16 @@ gtk_clist_remove (GtkCList * clist,
    * end of the list */
   if (row == clist->rows - 1)
     clist->row_list_end = list->prev;
+  if (row >= clist->focus_row && clist->focus_row >=0)
+    clist->focus_row--;
 
   clist->row_list = g_list_remove (clist->row_list, clist_row);
   clist->rows--;
-  sync_selection (clist, row, SYNC_REMOVE);
-
-  /* preform any selections required by the selection mode */
-  if (was_selected)
-    {
-      switch (clist->selection_mode)
-       {
-       case GTK_SELECTION_BROWSE:
-         if (row == clist->rows)
-           gtk_clist_select_row (clist, row - 1, -1);
-         else
-           gtk_clist_select_row (clist, row, -1);
-         break;
+  
+  if (row < ROW_FROM_YPIXEL (clist, 0))
+    clist->voffset += clist->row_height + CELL_SPACING;
 
-       default:
-         break;
-       }
-    }
+  sync_selection (clist, row, SYNC_REMOVE);
 
   /* toast the row */
   row_delete (clist, clist_row);
@@ -1404,54 +1881,79 @@ sync_selection (GtkCList * clist,
                gint mode)
 {
   GList *list;
-  
-  list = clist->selection;
-  while (list)
+  gint d;
+
+  if (mode == SYNC_INSERT)
+    d = 1;
+  else
+    d = -1;
+      
+  if (clist->focus_row >= row)
     {
-      if (GPOINTER_TO_INT (list->data) >= row)
-        switch (mode)
-          {
-          case SYNC_INSERT:
-            list->data = ((gchar*) list->data) + 1;
-            break;
+      clist->focus_row += d;
+      if (clist->focus_row == -1 && clist->rows >= 1)
+       clist->focus_row = 0;
+    }
 
-          case SYNC_REMOVE:
-            list->data = ((gchar*) list->data) - 1;
-            break;
+  if (clist->selection_mode == GTK_SELECTION_BROWSE && clist->anchor != -1)
+    GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
 
-          default:
-            break;
-          }
+  g_list_free (clist->undo_selection);
+  g_list_free (clist->undo_unselection);
+  clist->undo_selection = NULL;
+  clist->undo_unselection = NULL;
+
+  clist->anchor = -1;
+  clist->drag_pos = -1;
+  clist->undo_anchor = clist->focus_row;
 
+  list = clist->selection;
+  while (list)
+    {
+      if (GPOINTER_TO_INT (list->data) >= row)
+       list->data = ((gchar*) list->data) + d;
       list = list->next;
     }
 }
 
 void
 gtk_clist_clear (GtkCList * clist)
+{
+  GTK_CLIST_CLASS_FW (clist)->clear (clist);
+}
+
+static void
+real_clear (GtkCList * clist)
 {
   GList *list;
 
   g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
 
   /* remove all the rows */
   for (list = clist->row_list; list; list = list->next)
-    {
-      GtkCListRow *clist_row;
+    row_delete (clist, GTK_CLIST_ROW (list));
 
-      clist_row = list->data;
-      row_delete (clist, clist_row);
-    }
   g_list_free (clist->row_list);
 
   /* free up the selection list */
   g_list_free (clist->selection);
+  g_list_free (clist->undo_selection);
+  g_list_free (clist->undo_unselection);
 
   clist->row_list = NULL;
   clist->row_list_end = NULL;
   clist->selection = NULL;
+  clist->selection_end = NULL;
+  clist->undo_selection = NULL;
+  clist->undo_unselection = NULL;
   clist->voffset = 0;
   clist->rows = 0;
+  clist->focus_row = -1;
+  clist->anchor = -1;
+  clist->undo_anchor = -1;
+  clist->anchor_state = GTK_STATE_SELECTED;
+  clist->drag_pos = -1;
 
   /* zero-out the scrollbars */
   if (clist->vscrollbar)
@@ -1460,10 +1962,7 @@ gtk_clist_clear (GtkCList * clist)
       gtk_signal_emit_by_name (GTK_OBJECT (GTK_RANGE (clist->vscrollbar)->adjustment), "changed");
       
       if (!GTK_CLIST_FROZEN (clist))
-       {
-         adjust_scrollbars (clist);
-         draw_rows (clist, NULL);
-       }
+       gtk_clist_thaw (clist);
     }
 }
 
@@ -1477,6 +1976,7 @@ gtk_clist_swap_rows (GtkCList * clist,
   gpointer swap;
   
   g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
 
   if (row1 < 0 || row1 > (clist->rows - 1))
     return;
@@ -1509,12 +2009,12 @@ gtk_clist_swap_rows (GtkCList * clist,
   if (!GTK_CLIST_FROZEN (clist))
     {
       if (gtk_clist_row_is_visible (clist, row1) != GTK_VISIBILITY_NONE)
-       (GTK_CLIST_CLASS (GTK_OBJECT (clist)->klass)->draw_row)
-         (clist, NULL, row1, (GtkCListRow *) link2->data);
+       GTK_CLIST_CLASS_FW (clist)->draw_row
+         (clist, NULL, row1, GTK_CLIST_ROW (link2));
 
       if (gtk_clist_row_is_visible (clist, row2) != GTK_VISIBILITY_NONE)
-       (GTK_CLIST_CLASS (GTK_OBJECT (clist)->klass)->draw_row)
-         (clist, NULL, row2, (GtkCListRow *) link1->data);
+       GTK_CLIST_CLASS_FW (clist)->draw_row
+         (clist, NULL, row2, GTK_CLIST_ROW (link1));
     }
 }
 
@@ -1535,6 +2035,7 @@ gtk_clist_set_row_data_full (GtkCList * clist,
   GtkCListRow *clist_row;
 
   g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
 
   if (row < 0 || row > (clist->rows - 1))
     return;
@@ -1547,7 +2048,7 @@ gtk_clist_set_row_data_full (GtkCList * clist,
    * so the application can respond to the new data -- 
    * this could be questionable behavior */
   if (clist_row->state == GTK_STATE_SELECTED)
-    gtk_clist_select_row (clist, row, -1);
+    select_row (clist, row, -1, NULL);
 }
 
 gpointer
@@ -1557,6 +2058,7 @@ gtk_clist_get_row_data (GtkCList * clist,
   GtkCListRow *clist_row;
 
   g_return_val_if_fail (clist != NULL, NULL);
+  g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
 
   if (row < 0 || row > (clist->rows - 1))
     return NULL;
@@ -1578,21 +2080,9 @@ gtk_clist_find_row_from_data (GtkCList * clist,
   if (clist->rows < 1)
     return -1; /* is this an optimization or just worthless? */
 
-  n = 0;
-  list = clist->row_list;
-  while (list)
-    {
-      GtkCListRow *clist_row;
-
-      clist_row = list->data;
-      if (clist_row->data == data)
-        break;
-      n++;
-      list = list->next;
-    }
-
-  if (list)
-    return n;
+  for (n = 0, list = clist->row_list; list; n++, list = list->next)
+    if (GTK_CLIST_ROW (list)->data == data)
+      return n;
 
   return -1;
 }
@@ -1603,6 +2093,7 @@ gtk_clist_select_row (GtkCList * clist,
                      gint column)
 {
   g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
 
   if (row < 0 || row >= clist->rows)
     return;
@@ -1619,6 +2110,7 @@ gtk_clist_unselect_row (GtkCList * clist,
                        gint column)
 {
   g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
 
   if (row < 0 || row >= clist->rows)
     return;
@@ -1636,6 +2128,7 @@ gtk_clist_row_is_visible (GtkCList * clist,
   gint top;
 
   g_return_val_if_fail (clist != NULL, 0);
+  g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
 
   if (row < 0 || row >= clist->rows)
     return GTK_VISIBILITY_NONE;
@@ -1658,7 +2151,7 @@ gtk_clist_row_is_visible (GtkCList * clist,
   return GTK_VISIBILITY_FULL;
 }
 
-GtkAdjustment*
+GtkAdjustment *
 gtk_clist_get_vadjustment (GtkCList * clist)
 {
   g_return_val_if_fail (clist != NULL, NULL);
@@ -1667,7 +2160,7 @@ gtk_clist_get_vadjustment (GtkCList * clist)
   return gtk_range_get_adjustment (GTK_RANGE (clist->vscrollbar));
 }
 
-GtkAdjustment*
+GtkAdjustment *
 gtk_clist_get_hadjustment (GtkCList * clist)
 {
   g_return_val_if_fail (clist != NULL, NULL);
@@ -1701,6 +2194,69 @@ gtk_clist_set_policy (GtkCList * clist,
     }
 }
 
+void
+gtk_clist_undo_selection (GtkCList *clist)
+{
+  g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
+
+  if (clist->selection_mode == GTK_SELECTION_EXTENDED &&
+      (clist->undo_selection || clist->undo_unselection))
+    gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNDO_SELECTION]);
+}
+
+static void
+real_undo_selection (GtkCList *clist)
+{
+  GList *work;
+
+  g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
+
+  if (gdk_pointer_is_grabbed () ||
+      clist->selection_mode != GTK_SELECTION_EXTENDED)
+    return;
+
+  if (clist->anchor >= 0)
+    resync_selection (clist, NULL);
+
+  if (!(clist->undo_selection || clist->undo_unselection))
+    {
+      gtk_clist_unselect_all (clist);
+      return;
+    }
+
+  for (work = clist->undo_selection; work; work = work->next)
+    gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
+                    GPOINTER_TO_INT (work->data), -1, NULL);
+
+  for (work = clist->undo_unselection; work; work = work->next)
+    gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW], 
+                    GPOINTER_TO_INT (work->data), -1, NULL);
+
+  if (GTK_WIDGET_HAS_FOCUS (clist) && clist->focus_row != clist->undo_anchor)
+    {
+      gtk_clist_draw_focus (GTK_WIDGET (clist));
+      clist->focus_row = clist->undo_anchor;
+      gtk_clist_draw_focus (GTK_WIDGET (clist));
+    }
+  else
+    clist->focus_row = clist->undo_anchor;
+  
+  clist->undo_anchor = -1;
+  g_list_free (clist->undo_selection);
+  g_list_free (clist->undo_unselection);
+  clist->undo_selection = NULL;
+  clist->undo_unselection = NULL;
+
+  if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
+      clist->clist_window_height)
+    gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
+  else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
+    gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
+}
+
 /*
  * GTKOBJECT
  *   gtk_clist_destroy
@@ -1742,6 +2298,17 @@ gtk_clist_destroy (GtkObject * object)
       clist->hscrollbar = NULL;
     }
 
+  if (clist->htimer)
+    {
+      gtk_timeout_remove (clist->htimer);
+      clist->htimer = 0;
+    }
+  if (clist->vtimer)
+    {
+      gtk_timeout_remove (clist->vtimer);
+      clist->vtimer = 0;
+    }
+
   /* destroy the column buttons */
   for (i = 0; i < clist->columns; i++)
     if (clist->column[i].button)
@@ -1820,10 +2387,10 @@ gtk_clist_realize (GtkWidget * widget)
   attributes.event_mask |= (GDK_EXPOSURE_MASK |
                            GDK_BUTTON_PRESS_MASK |
                            GDK_BUTTON_RELEASE_MASK |
-                           GDK_KEY_PRESS_MASK);
+                           GDK_KEY_PRESS_MASK |
+                           GDK_KEY_RELEASE_MASK);
   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
 
-
   /* main window */
   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
   gdk_window_set_user_data (widget->window, clist);
@@ -1870,7 +2437,8 @@ gtk_clist_realize (GtkWidget * widget)
   attributes.event_mask = (GDK_BUTTON_PRESS_MASK |
                           GDK_BUTTON_RELEASE_MASK |
                           GDK_POINTER_MOTION_MASK |
-                          GDK_POINTER_MOTION_HINT_MASK);
+                          GDK_POINTER_MOTION_HINT_MASK |
+                          GDK_KEY_PRESS_MASK);
   attributes.cursor = clist->cursor_drag = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
   attributes_mask = GDK_WA_CURSOR;
   
@@ -2104,7 +2672,10 @@ gtk_clist_button_press (GtkWidget * widget,
 {
   gint i;
   GtkCList *clist;
-  gint x, y, row, column;
+  gint x;
+  gint y;
+  gint row;
+  gint column;
 
   g_return_val_if_fail (widget != NULL, FALSE);
   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
@@ -2112,6 +2683,10 @@ gtk_clist_button_press (GtkWidget * widget,
 
   clist = GTK_CLIST (widget);
 
+  /* we don't handle button 2 and 3 */
+  if (event->button != 1)
+    return FALSE;
+  
   /* selections on the list */
   if (event->window == clist->clist_window)
     {
@@ -2119,8 +2694,102 @@ gtk_clist_button_press (GtkWidget * widget,
       y = event->y;
 
       if (get_selection_info (clist, x, y, &row, &column))
-       toggle_row (clist, row, column, event);
+       {
+         gint old_row = clist->focus_row;
+         gboolean no_focus_row = FALSE;
+
+         if (clist->focus_row == -1)
+           {
+             old_row = row;
+             no_focus_row = TRUE;
+           }
+
+         GTK_CLIST_SET_FLAG (clist, CLIST_DRAG_SELECTION);
+         gdk_pointer_grab (clist->clist_window, FALSE,
+                           GDK_POINTER_MOTION_HINT_MASK |
+                           GDK_BUTTON1_MOTION_MASK |
+                           GDK_BUTTON_RELEASE_MASK,
+                           NULL, NULL, event->time);
+
+
+         if (GTK_CLIST_ADD_MODE (clist))
+           {
+             GTK_CLIST_UNSET_FLAG (clist, CLIST_ADD_MODE);
+             if (GTK_WIDGET_HAS_FOCUS (widget))
+               {
+                 gtk_clist_draw_focus (widget);
+                 gdk_gc_set_line_attributes (clist->xor_gc, 1,
+                                             GDK_LINE_SOLID, 0, 0);
+                 clist->focus_row = row;
+                 gtk_clist_draw_focus (widget);
+               }
+             else
+               {
+                 gdk_gc_set_line_attributes (clist->xor_gc, 1,
+                                             GDK_LINE_SOLID, 0, 0);
+                 clist->focus_row = row;
+               }
+           }
+         else if (row != clist->focus_row)
+           {
+             if (GTK_WIDGET_HAS_FOCUS (widget))
+               {
+                 gtk_clist_draw_focus (widget);
+                 clist->focus_row = row;
+                 gtk_clist_draw_focus (widget);
+               }
+             else
+               clist->focus_row = row;
+           }
+
+         if (!GTK_WIDGET_HAS_FOCUS (widget))
+           gtk_widget_grab_focus (widget);
+
+         switch (clist->selection_mode)
+           {
+           case GTK_SELECTION_SINGLE:
+           case GTK_SELECTION_MULTIPLE:
+             clist->anchor = row;
+             break;
+
+           case GTK_SELECTION_BROWSE:
+             if (row != old_row || no_focus_row)
+               select_row (clist, row, column, (GdkEvent *) event);
+             break;
+
+           case GTK_SELECTION_EXTENDED:
+             if (event->state & GDK_CONTROL_MASK)
+               {
+                 if (event->state & GDK_SHIFT_MASK)
+                   {
+                     set_anchor (clist, TRUE, old_row, old_row);
+                     update_extended_selection (clist, clist->focus_row);
+                   }
+                 else
+                   {
+                     if (clist->anchor == -1)
+                       set_anchor (clist, TRUE, row, old_row);
+                     else
+                       update_extended_selection (clist, clist->focus_row);
+                   }
+                 return TRUE;
+               }
+             if (event->state & GDK_SHIFT_MASK)
+               {
+                 set_anchor (clist, FALSE, old_row, old_row);
+                 update_extended_selection (clist, clist->focus_row);
+                 return TRUE;
+               }
+             if (clist->anchor == -1)
+               set_anchor (clist, FALSE, row, old_row);
+             else
+               update_extended_selection (clist, clist->focus_row);
+             return TRUE;
 
+           default:
+             break;
+           }
+       }
       return FALSE;
     }
 
@@ -2137,6 +2806,9 @@ gtk_clist_button_press (GtkWidget * widget,
                          GDK_BUTTON_RELEASE_MASK,
                          NULL, NULL, event->time);
 
+       if (GTK_CLIST_ADD_MODE (clist))
+         gdk_gc_set_line_attributes (clist->xor_gc, 1, GDK_LINE_SOLID, 0, 0);
+
        draw_xor_line (clist);
        return FALSE;
       }
@@ -2157,6 +2829,10 @@ gtk_clist_button_release (GtkWidget * widget,
 
   clist = GTK_CLIST (widget);
 
+  /* we don't handle button 2 and 3 */
+  if (event->button != 1)
+    return FALSE;
+
   /* release on resize windows */
   if (GTK_CLIST_IN_DRAG (clist))
     for (i = 0; i < clist->columns; i++)
@@ -2166,22 +2842,156 @@ gtk_clist_button_release (GtkWidget * widget,
          gtk_widget_get_pointer (widget, &x, NULL);
          width = new_column_width (clist, i, &x, &visible);
          gdk_pointer_ungrab (event->time);
-         
+
          if (visible)
            draw_xor_line (clist);
 
+         if (GTK_CLIST_ADD_MODE (clist))
+           {
+             gdk_gc_set_line_attributes (clist->xor_gc, 1, 
+                                         GDK_LINE_ON_OFF_DASH, 0, 0);
+             gdk_gc_set_dashes (clist->xor_gc, 0, "\4\4", 2);
+           }
+
          resize_column (clist, i, width);
          return FALSE;
        }
 
+  if (GTK_CLIST_DRAG_SELECTION (clist))
+    {
+      gint row;
+      gint column;
+
+      GTK_CLIST_UNSET_FLAG (clist, CLIST_DRAG_SELECTION);
+      gdk_pointer_ungrab (event->time);
+      if (clist->htimer)
+       {
+         gtk_timeout_remove (clist->htimer);
+         clist->htimer = 0;
+       }
+      if (clist->vtimer)
+       {
+         gtk_timeout_remove (clist->vtimer);
+         clist->vtimer = 0;
+       }
+      switch (clist->selection_mode)
+       {
+       case GTK_SELECTION_EXTENDED:
+         if (!(event->state & GDK_SHIFT_MASK) ||
+             event->x < 0 || event->x >= clist->clist_window_width ||
+             event->y < 0 || event->y >= clist->clist_window_height)
+           GTK_CLIST_CLASS_FW (clist)->resync_selection
+             (clist, (GdkEvent *) event);
+         break;
+
+       case GTK_SELECTION_SINGLE:
+       case GTK_SELECTION_MULTIPLE:
+         if (get_selection_info (clist, event->x, event->y, &row, &column))
+           {
+             if (clist->anchor == clist->focus_row)
+               toggle_row (clist, row, column, (GdkEvent *) event);
+           }
+         clist->anchor = -1;
+         break;
+
+       default:
+         break;
+       }
+    }
+
+  return FALSE;
+}
+
+static gint
+horizontal_timeout (GtkCList *clist)
+{
+  gint x, y;
+  GdkEventMotion event;
+  GdkModifierType mask;
+
+  g_return_val_if_fail (GTK_IS_CLIST (clist), FALSE);
+
+  clist->htimer = 0;
+  gdk_window_get_pointer (clist->clist_window, &x, &y, &mask);
+
+  event.is_hint = 0;
+  event.x = x;
+  event.y = y;
+  event.state = mask;
+
+  gtk_clist_motion (GTK_WIDGET (clist), &event);
+
+  return FALSE;
+}
+
+static gint
+vertical_timeout (GtkCList *clist)
+{
+  gint x, y;
+  GdkEventMotion event;
+  GdkModifierType mask;
+
+  g_return_val_if_fail (GTK_IS_CLIST (clist), FALSE);
+
+  clist->vtimer = 0;
+  gdk_window_get_pointer (clist->clist_window, &x, &y, &mask);
+
+  event.is_hint = 0;
+  event.x = x;
+  event.y = y;
+  event.state = mask;
+
+  gtk_clist_motion (GTK_WIDGET (clist), &event);
+
   return FALSE;
 }
 
+static void
+move_vertical (GtkCList *clist,
+              gint row,
+              gfloat align)
+{
+  gint y;
+  GtkAdjustment *adj;
+
+  adj = GTK_RANGE (clist->vscrollbar)->adjustment;
+
+  y = ROW_TOP_YPIXEL (clist, row) - clist->voffset;
+  
+  y = y - align * (clist->clist_window_height - clist->row_height)
+    + (2 * align - 1) * CELL_SPACING;
+  
+  if (y + adj->page_size > adj->upper)
+    adj->value = adj->upper - adj->page_size;
+  else
+    adj->value = y;
+
+  gtk_signal_emit_by_name (GTK_OBJECT (adj), "value_changed");
+}
+
+static void
+move_horizontal (GtkCList *clist,
+                gint diff)
+{
+  gfloat upper;
+  GtkAdjustment *adj;
+
+  adj = GTK_RANGE (clist->hscrollbar)->adjustment;
+
+  adj->value += diff;
+
+  upper = adj->upper - adj->page_size;
+  adj->value = MIN (adj->value, upper);
+  adj->value = MAX (adj->value, 0.0);
+
+  gtk_signal_emit_by_name (GTK_OBJECT (adj), "value_changed");
+}
+
 static gint
 gtk_clist_motion (GtkWidget * widget,
                  GdkEventMotion * event)
 {
-  gint i, x, visible;
+  gint i, x, y, visible;
   GtkCList *clist;
 
   g_return_val_if_fail (widget != NULL, FALSE);
@@ -2217,6 +3027,155 @@ gtk_clist_motion (GtkWidget * widget,
            }
        }
 
+  if (GTK_CLIST_DRAG_SELECTION (clist))
+    {
+      gint row;
+
+      if (event->is_hint || event->window != clist->clist_window)
+       gdk_window_get_pointer (clist->clist_window, &x, &y, NULL);
+
+      /* horizontal autoscrolling */
+      if (LIST_WIDTH (clist) > clist->clist_window_width &&
+         (x < 0 || x >= clist->clist_window_width))
+       {
+         if (clist->htimer == 0)
+           {
+             clist->htimer = gtk_timeout_add
+               (SCROLL_TIME, (GtkFunction) horizontal_timeout, clist);
+
+             if (!((x < 0 && GTK_RANGE (clist->hscrollbar)->adjustment->value 
+                  == 0) || (x >= clist->clist_window_width &&
+                 GTK_RANGE (clist->hscrollbar)->adjustment->value ==
+                 LIST_WIDTH (clist) - clist->clist_window_width)))
+               {
+                 if (x < 0)
+                   move_horizontal (clist, -1 + (x/2));
+                 else
+                   move_horizontal (clist, 
+                                    1 + (x - clist->clist_window_width) / 2);
+               }
+           }
+         else
+           return TRUE;
+       }
+
+      row = ROW_FROM_YPIXEL (clist, y);
+
+      /* don't scroll on last pixel row if it's a cell spacing */
+      if (y == clist->clist_window_height-1 &&
+         y == ROW_TOP_YPIXEL (clist, row-1) + clist->row_height)
+       return TRUE;
+
+      /* vertical autoscrolling */
+      if (LIST_HEIGHT (clist) > clist->clist_window_height &&
+         (y < 0 || y >= clist->clist_window_height))
+       {
+         if (clist->vtimer == 0)
+           {
+             clist->vtimer = gtk_timeout_add (SCROLL_TIME, 
+                                       (GtkFunction) vertical_timeout,
+                                        clist);
+
+             if ((y < 0 && clist->focus_row == 0) || 
+                 (y >= clist->clist_window_height && 
+                  clist->focus_row == clist->rows-1))
+               return TRUE;
+
+             if (row < 0 && clist->focus_row > 0)
+               {
+                 gtk_clist_draw_focus (widget);
+                 clist->focus_row = 0;
+                 gtk_clist_draw_focus (widget);
+               }
+             else if (row > clist->rows - 1 && clist->focus_row 
+                      < clist->rows - 1)
+               {
+                 gtk_clist_draw_focus (widget);
+                 clist->focus_row = clist->rows - 1;
+                 gtk_clist_draw_focus (widget);
+               }
+             else if (row >= 0 && row <= clist->rows - 1)
+               {
+                 gtk_clist_draw_focus (widget);
+                 clist->focus_row = row;
+                 gtk_clist_draw_focus (widget);
+               }
+             else
+               return TRUE;
+
+             switch (clist->selection_mode)
+               {
+               case GTK_SELECTION_BROWSE:
+                 select_row (clist, clist->focus_row, -1, (GdkEvent *) event);
+                 break;
+
+               case GTK_SELECTION_EXTENDED:
+                 update_extended_selection (clist, clist->focus_row);
+                 break;
+
+               default:
+                 break;
+               }
+
+             if (y < 0)
+               move_vertical (clist, row, 0);
+             else
+               move_vertical (clist, row, 1);
+           }
+         else
+           return TRUE;
+       }
+
+      if (row == clist->focus_row)
+       return TRUE;
+      
+      /* dragging inside clist_window */
+      if (row < 0 && clist->focus_row > 0)
+       {
+         gtk_clist_draw_focus (widget);
+         clist->focus_row = 0;
+         gtk_clist_draw_focus (widget);
+       }
+      else if (row > clist->rows-1 && clist->focus_row < clist->rows-1)
+       {
+         gtk_clist_draw_focus (widget);
+         clist->focus_row = clist->rows-1;
+         gtk_clist_draw_focus (widget);
+       }
+      else if (row >= 0 && row <= clist->rows-1)
+       {
+         gtk_clist_draw_focus (widget);
+         clist->focus_row = row;
+         gtk_clist_draw_focus (widget);
+       }
+      else
+       return TRUE;
+      
+      switch (clist->selection_mode)
+       {
+       case GTK_SELECTION_EXTENDED:
+         update_extended_selection (clist, clist->focus_row);
+         return TRUE;
+         
+       case  GTK_SELECTION_BROWSE:
+         select_row (clist, clist->focus_row, -1, (GdkEvent *) event);
+         break;
+         
+       default:
+         break;
+       }
+         
+      if (ROW_TOP_YPIXEL(clist, clist->focus_row) + clist->row_height <= 0)
+       gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
+      else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
+       gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
+      else if (ROW_TOP_YPIXEL (clist, clist->focus_row) >=
+              clist->clist_window_height)
+       gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
+      else if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
+              clist->clist_window_height)
+       gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
+    }
   return TRUE;
 }
 
@@ -2854,6 +3813,26 @@ draw_row (GtkCList * clist,
          break;
        }
     }
+  if (clist->focus_row == row && GTK_WIDGET_HAS_FOCUS (widget))
+    {
+      if (area)
+       {
+         if (gdk_rectangle_intersect (area, &row_rectangle,
+                                      &intersect_rectangle))
+           {
+             gdk_gc_set_clip_rectangle (clist->xor_gc, &intersect_rectangle);
+             gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
+                                 row_rectangle.x, row_rectangle.y,
+                                 row_rectangle.width - 1,
+                                 row_rectangle.height - 1);
+             gdk_gc_set_clip_rectangle (clist->xor_gc, NULL);
+           }
+       }
+      else
+       gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
+                           row_rectangle.x, row_rectangle.y,
+                           row_rectangle.width - 1, row_rectangle.height - 1);
+    }
 }
 
 static void
@@ -2898,8 +3877,7 @@ draw_rows (GtkCList * clist,
       if (i > last_row)
        return;
 
-      (GTK_CLIST_CLASS (GTK_OBJECT (clist)->klass)->draw_row)
-       (clist, area, i, clist_row);
+      GTK_CLIST_CLASS_FW (clist)->draw_row (clist, area, i, clist_row);
       i++;
     }
 
@@ -3006,75 +3984,25 @@ static void
 toggle_row (GtkCList * clist,
            gint row,
            gint column,
-           GdkEventButton * event)
+           GdkEvent * event)
 {
-  gint i;
-  GList *list;
-  GtkCListRow *clist_row, *selected_row;
-
-  i = 0;
-  list = clist->row_list;
-  selected_row = NULL;
+  GtkCListRow *clist_row;
 
   switch (clist->selection_mode)
     {
+    case GTK_SELECTION_EXTENDED:
+    case GTK_SELECTION_MULTIPLE:
     case GTK_SELECTION_SINGLE:
-      while (list)
-       {
-         clist_row = list->data;
-         list = list->next;
-
-         if (row == i)
-           selected_row = clist_row;
-         else if (clist_row->state == GTK_STATE_SELECTED)
-           gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW], 
-                            i, column, event);
-
-         i++;
-       }
-
-      if (selected_row && selected_row->state == GTK_STATE_SELECTED)
-       gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW], 
-                        row, column, event);
-      else
-       gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW], 
-                        row, column, event);
-      break;
-
 
-    case GTK_SELECTION_BROWSE:
-      while (list)
+      clist_row = g_list_nth (clist->row_list, row)->data;
+      if (clist_row->state == GTK_STATE_SELECTED)
        {
-         clist_row = list->data;
-         list = list->next;
-
-         if (i != row && clist_row->state == GTK_STATE_SELECTED)
-           gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW], 
-                            i, column, event);
-         i++;
+         unselect_row (clist, row, column, event);
+         return;
        }
 
-      gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW], 
-                      row, column, event);
-      break;
-
-
-    case GTK_SELECTION_MULTIPLE:
-      clist_row = (g_list_nth (clist->row_list, row))->data;
-
-      if (clist_row->state == GTK_STATE_SELECTED)
-       gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW], 
-                        row, column, event);
-      else
-       gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW], 
-                        row, column, event);
-      break;
-
-
-    case GTK_SELECTION_EXTENDED:
-      break;
-
-    default:
+    case GTK_SELECTION_BROWSE:
+      select_row (clist, row, column, event);
       break;
     }
 }
@@ -3083,107 +4011,97 @@ static void
 select_row (GtkCList * clist,
            gint row,
            gint column,
-           GdkEventButton * event)
+           GdkEvent * event)
 {
-  gint i;
-  GList *list;
-  GtkCListRow *clist_row;
-
-  switch (clist->selection_mode)
-    {
-    case GTK_SELECTION_SINGLE:
-    case GTK_SELECTION_BROWSE:
-      i = 0;
-      list = clist->row_list;
-      while (list)
-       {
-         clist_row = list->data;
-         list = list->next;
-
-         if (row != i && clist_row->state == GTK_STATE_SELECTED)
-           gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW], 
-                            i, column, event);
-
-         i++;
-       }
-
-      gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW], 
-                      row, column, event);
-      break;
-
-    case GTK_SELECTION_MULTIPLE:
-      gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW], 
-                      row, column, event);
-
-      break;
-
-    case GTK_SELECTION_EXTENDED:
-      break;
-
-    default:
-      break;
-    }
+  gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
+                  row, column, event);
 }
 
 static void
 unselect_row (GtkCList * clist,
              gint row,
              gint column,
-             GdkEventButton * event)
+             GdkEvent * event)
 {
-  switch (clist->selection_mode)
-    {
-    case GTK_SELECTION_SINGLE:
-    case GTK_SELECTION_BROWSE:
-    case GTK_SELECTION_MULTIPLE:
-      gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW], 
-                      row, column, event);
-      break;
-
-    case GTK_SELECTION_EXTENDED:
-      break;
-
-    default:
-      break;
-    }
+  gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW],
+                  row, column, event);
 }
 
 static void
 real_select_row (GtkCList * clist,
                 gint row,
                 gint column,
-                GdkEventButton * event)
+                GdkEvent * event)
 {
   GtkCListRow *clist_row;
+  GList *list;
+  gint sel_row;
+  gboolean row_selected;
 
   g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
 
   if (row < 0 || row > (clist->rows - 1))
     return;
 
+  switch (clist->selection_mode)
+    {
+    case GTK_SELECTION_SINGLE:
+    case GTK_SELECTION_BROWSE:
+
+      row_selected = FALSE;
+      list = clist->selection;
+
+      while (list)
+       {
+         sel_row = GPOINTER_TO_INT (list->data);
+         list = list->next;
+
+         if (row == sel_row)
+           row_selected = TRUE;
+         else
+           gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW], 
+                            sel_row, column, event);
+       }
+
+      if (row_selected)
+       return;
+      
+    default:
+      break;
+    }
+
   clist_row = (g_list_nth (clist->row_list, row))->data;
 
-  if (clist_row->state == GTK_STATE_NORMAL)
-    {
-      clist_row->state = GTK_STATE_SELECTED;
-      clist->selection = g_list_append (clist->selection, GINT_TO_POINTER (row));
+  if (clist_row->state != GTK_STATE_NORMAL)
+    return;
 
-      if (!GTK_CLIST_FROZEN (clist)
-         && (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
-       (GTK_CLIST_CLASS (GTK_OBJECT (clist)->klass)->draw_row)
-         (clist, NULL, row, clist_row);
+  clist_row->state = GTK_STATE_SELECTED;
+  if (!clist->selection)
+    {
+      clist->selection = g_list_append (clist->selection,
+                                       GINT_TO_POINTER (row));
+      clist->selection_end = clist->selection;
     }
+  else
+    clist->selection_end = 
+      g_list_append (clist->selection_end, GINT_TO_POINTER (row))->next;
+  
+  if (!GTK_CLIST_FROZEN (clist)
+      && (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
+    GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row, clist_row);
 }
 
 static void
 real_unselect_row (GtkCList * clist,
                   gint row,
                   gint column,
-                  GdkEventButton * event)
+                  GdkEvent * event)
 {
   GtkCListRow *clist_row;
 
   g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
 
   if (row < 0 || row > (clist->rows - 1))
     return;
@@ -3193,12 +4111,17 @@ real_unselect_row (GtkCList * clist,
   if (clist_row->state == GTK_STATE_SELECTED)
     {
       clist_row->state = GTK_STATE_NORMAL;
-      clist->selection = g_list_remove (clist->selection, GINT_TO_POINTER (row));
 
+      if (clist->selection_end && 
+         clist->selection_end->data == GINT_TO_POINTER (row))
+       clist->selection_end = clist->selection_end->prev;
+
+      clist->selection = g_list_remove (clist->selection,
+                                       GINT_TO_POINTER (row));
+      
       if (!GTK_CLIST_FROZEN (clist)
          && (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
-       (GTK_CLIST_CLASS (GTK_OBJECT (clist)->klass)->draw_row)
-         (clist, NULL, row, clist_row);
+       GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row, clist_row);
     }
 }
 
@@ -3212,6 +4135,7 @@ get_selection_info (GtkCList * clist,
   gint trow, tcol;
 
   g_return_val_if_fail (clist != NULL, 0);
+  g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
 
   /* bounds checking, return false if the user clicked 
    * on a blank area */
@@ -3331,8 +4255,7 @@ column_button_create (GtkCList * clist,
   if (GTK_WIDGET_REALIZED (clist) && clist->title_window)
     gtk_widget_set_parent_window (clist->column[column].button, clist->title_window);
   
-  gtk_signal_connect (GTK_OBJECT (button),
-                     "clicked",
+  gtk_signal_connect (GTK_OBJECT (button), "clicked",
                      (GtkSignalFunc) column_button_clicked,
                      (gpointer) clist);
 
@@ -3376,6 +4299,7 @@ create_scrollbars (GtkCList * clist)
   GtkAdjustment *adjustment;
 
   clist->vscrollbar = gtk_vscrollbar_new (NULL);
+
   adjustment = gtk_range_get_adjustment (GTK_RANGE (clist->vscrollbar));
 
   gtk_signal_connect (GTK_OBJECT (adjustment), "changed",
@@ -3390,6 +4314,7 @@ create_scrollbars (GtkCList * clist)
   gtk_widget_show (clist->vscrollbar);
 
   clist->hscrollbar = gtk_hscrollbar_new (NULL);
+
   adjustment = gtk_range_get_adjustment (GTK_RANGE (clist->hscrollbar));
 
   gtk_signal_connect (GTK_OBJECT (adjustment), "changed",
@@ -3614,7 +4539,10 @@ hadjustment_value_changed (GtkAdjustment * adjustment,
 {
   GtkCList *clist;
   GdkRectangle area;
-  gint i, diff, value;
+  gint i;
+  gint y = 0;
+  gint diff = 0;
+  gint value;
 
   g_return_if_fail (adjustment != NULL);
   g_return_if_fail (data != NULL);
@@ -3622,97 +4550,151 @@ hadjustment_value_changed (GtkAdjustment * adjustment,
 
   clist = GTK_CLIST (data);
 
-  if (!GTK_WIDGET_DRAWABLE (clist))
+  if (!GTK_WIDGET_DRAWABLE (clist) ||
+      adjustment != gtk_range_get_adjustment (GTK_RANGE (clist->hscrollbar)))
     return;
 
   value = adjustment->value;
-
-  if (adjustment == gtk_range_get_adjustment (GTK_RANGE (clist->hscrollbar)))
+  
+  /* move the column buttons and resize windows */
+  for (i = 0; i < clist->columns; i++)
     {
-      /* move the column buttons and resize windows */
-      for (i = 0; i < clist->columns; i++)
+      if (clist->column[i].button)
        {
-         if (clist->column[i].button)
+         clist->column[i].button->allocation.x -= value + clist->hoffset;
+         
+         if (clist->column[i].button->window)
            {
-             clist->column[i].button->allocation.x -= value + clist->hoffset;
-
-             if (clist->column[i].button->window)
-               {
-                 gdk_window_move (clist->column[i].button->window,
-                                  clist->column[i].button->allocation.x,
-                                  clist->column[i].button->allocation.y);
-
-                 if (clist->column[i].window)
-                   gdk_window_move (clist->column[i].window,
-                                    clist->column[i].button->allocation.x +
-                                    clist->column[i].button->allocation.width - 
-                                    (DRAG_WIDTH / 2), 0); 
-               }
+             gdk_window_move (clist->column[i].button->window,
+                              clist->column[i].button->allocation.x,
+                              clist->column[i].button->allocation.y);
+             
+             if (clist->column[i].window)
+               gdk_window_move (clist->column[i].window,
+                                clist->column[i].button->allocation.x +
+                                clist->column[i].button->allocation.width - 
+                                (DRAG_WIDTH / 2), 0); 
            }
        }
+    }
 
-      if (value > -clist->hoffset)
+  if (value > -clist->hoffset)
+    {
+      /* scroll right */
+      diff = value + clist->hoffset;
+      
+      clist->hoffset = -value;
+      
+      /* we have to re-draw the whole screen here... */
+      if (diff >= clist->clist_window_width)
        {
-         /* scroll right */
-         diff = value + clist->hoffset;
+         draw_rows (clist, NULL);
+         return;
+       }
 
-         /* we have to re-draw the whole screen here... */
-         if (diff >= clist->clist_window_width)
-           {
-             clist->hoffset = -value;
-             draw_rows (clist, NULL);
-             return;
-           }
+      if (GTK_WIDGET_HAS_FOCUS (clist) && !GTK_CLIST_CHILD_HAS_FOCUS (clist) &&
+         GTK_CLIST_ADD_MODE (clist))
+       {
+         y = ROW_TOP_YPIXEL (clist, clist->focus_row);
+             
+         gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE, 0, y,
+                             clist->clist_window_width - 1,
+                             clist->row_height - 1);
+       }
+      gdk_window_copy_area (clist->clist_window,
+                           clist->fg_gc,
+                           0, 0,
+                           clist->clist_window,
+                           diff,
+                           0,
+                           clist->clist_window_width - diff,
+                           clist->clist_window_height);
+
+      area.x = clist->clist_window_width - diff;
+    }
+  else
+    {
+      /* scroll left */
+      if (!(diff = -clist->hoffset - value))
+       return;
 
-         if ((diff != 0) && (diff != clist->clist_window_width))
-           gdk_window_copy_area (clist->clist_window,
-                                 clist->fg_gc,
-                                 0, 0,
-                                 clist->clist_window,
-                                 diff,
-                                 0,
-                                 clist->clist_window_width - diff,
-                                 clist->clist_window_height);
+      clist->hoffset = -value;
+      
+      /* we have to re-draw the whole screen here... */
+      if (diff >= clist->clist_window_width)
+       {
+         draw_rows (clist, NULL);
+         return;
+       }
+      
+      if (GTK_WIDGET_HAS_FOCUS (clist) && !GTK_CLIST_CHILD_HAS_FOCUS (clist) &&
+         GTK_CLIST_ADD_MODE (clist))
+       {
+         y = ROW_TOP_YPIXEL (clist, clist->focus_row);
+         
+         gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE, 0, y,
+                             clist->clist_window_width - 1,
+                             clist->row_height - 1);
+       }
 
-         area.x = clist->clist_window_width - diff;
-         area.y = 0;
-         area.width = diff;
-         area.height = clist->clist_window_height;
+      gdk_window_copy_area (clist->clist_window,
+                           clist->fg_gc,
+                           diff, 0,
+                           clist->clist_window,
+                           0,
+                           0,
+                           clist->clist_window_width - diff,
+                           clist->clist_window_height);
+         
+      area.x = 0;
+    }
+
+  area.y = 0;
+  area.width = diff;
+  area.height = clist->clist_window_height;
+
+  check_exposures (clist);
+
+  if (GTK_WIDGET_HAS_FOCUS (clist) && !GTK_CLIST_CHILD_HAS_FOCUS (clist))
+    {
+      if (GTK_CLIST_ADD_MODE (clist))
+       {
+         gint focus_row;
+         
+         focus_row = clist->focus_row;
+         clist->focus_row = -1;
+         draw_rows (clist, &area);
+         clist->focus_row = focus_row;
+         
+         gdk_draw_rectangle (clist->clist_window, clist->xor_gc,
+                             FALSE, 0, y, clist->clist_window_width - 1,
+                             clist->row_height - 1);
+         return;
        }
       else
        {
-         /* scroll left */
-         diff = -clist->hoffset - value;
-
-         /* we have to re-draw the whole screen here... */
-         if (diff >= clist->clist_window_width)
+         gint x0;
+         gint x1;
+         
+         if (area.x == 0)
            {
-             clist->hoffset = -value;
-             draw_rows (clist, NULL);
-             return;
+             x0 = clist->clist_window_width - 1;
+             x1 = diff;
            }
-
-         if ((diff != 0) && (diff != clist->clist_window_width))
-           gdk_window_copy_area (clist->clist_window,
-                                 clist->fg_gc,
-                                 diff, 0,
-                                 clist->clist_window,
-                                 0,
-                                 0,
-                                 clist->clist_window_width - diff,
-                                 clist->clist_window_height);
-
-         area.x = 0;
-         area.y = 0;
-         area.width = diff;
-         area.height = clist->clist_window_height;
+         else
+           {
+             x0 = 0;
+             x1 = area.x - 1;
+           }
+         
+         y = ROW_TOP_YPIXEL (clist, clist->focus_row);
+         gdk_draw_line (clist->clist_window, clist->xor_gc,
+                        x0, y + 1, x0, y + clist->row_height - 2);
+         gdk_draw_line (clist->clist_window, clist->xor_gc,
+                        x1, y + 1, x1, y + clist->row_height - 2);
+         
        }
-
-      clist->hoffset = -value;
-      if ((diff != 0) && (diff != clist->clist_window_width))
-       check_exposures (clist);
     }
-
   draw_rows (clist, &area);
 }
 
@@ -3935,3 +4917,1172 @@ add_style_data (GtkCList * clist)
 
   /* Column widths */
 }
+
+
+/* focus functions */
+
+static void
+gtk_clist_draw_focus (GtkWidget *widget)
+{
+  GtkCList *clist;
+
+  g_return_if_fail (widget != NULL);
+  g_return_if_fail (GTK_IS_CLIST (widget));
+
+  if (!GTK_WIDGET_DRAWABLE (widget))
+    return;
+
+  clist = GTK_CLIST (widget);
+  if (clist->focus_row >= 0)
+    gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
+                       0, ROW_TOP_YPIXEL(clist, clist->focus_row),
+                       clist->clist_window_width - 1,
+                       clist->row_height - 1);
+}
+
+static void
+gtk_clist_set_focus_child (GtkContainer *container,
+                          GtkWidget    *child)
+{
+  g_return_if_fail (container != NULL);
+  g_return_if_fail (GTK_IS_CLIST (container));
+
+  if (child)
+    {
+      g_return_if_fail (GTK_IS_WIDGET (child));
+      GTK_CLIST_SET_FLAG (GTK_CLIST (container), CLIST_CHILD_HAS_FOCUS);
+    }
+
+  parent_class->set_focus_child (container, child);
+}
+
+static gint
+gtk_clist_focus_in (GtkWidget     *widget,
+                   GdkEventFocus *event)
+{
+  GtkCList *clist;
+
+  g_return_val_if_fail (widget != NULL, FALSE);
+  g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
+  g_return_val_if_fail (event != NULL, FALSE);
+
+  GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
+  GTK_CLIST_UNSET_FLAG (widget, CLIST_CHILD_HAS_FOCUS);
+
+  clist = GTK_CLIST (widget);
+
+  if (clist->selection_mode == GTK_SELECTION_BROWSE &&
+      clist->selection == NULL && clist->focus_row > -1)
+    select_row (clist, clist->focus_row, -1, (GdkEvent *) event);
+  else
+    gtk_widget_draw_focus (widget);
+
+  return FALSE;
+}
+
+static gint
+gtk_clist_focus_out (GtkWidget     *widget,
+                    GdkEventFocus *event)
+{
+  g_return_val_if_fail (widget != NULL, FALSE);
+  g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
+  g_return_val_if_fail (event != NULL, FALSE);
+
+  GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
+  gtk_widget_draw_focus (widget);
+
+  if (GTK_CLIST (widget)->anchor != -1)
+    GTK_CLIST_CLASS_FW (widget)->resync_selection
+      (GTK_CLIST (widget), (GdkEvent *) event);
+
+  return FALSE;
+}
+
+static void
+toggle_add_mode (GtkCList *clist)
+{
+  g_return_if_fail (clist != 0);
+  g_return_if_fail (GTK_IS_CLIST (clist));
+  
+  if (gdk_pointer_is_grabbed () ||
+      clist->selection_mode != GTK_SELECTION_EXTENDED)
+    return;
+
+  gtk_clist_draw_focus (GTK_WIDGET (clist));
+  if (!GTK_CLIST_ADD_MODE (clist))
+    {
+      GTK_CLIST_SET_FLAG (clist, CLIST_ADD_MODE);
+      gdk_gc_set_line_attributes (clist->xor_gc, 1,
+                                 GDK_LINE_ON_OFF_DASH, 0, 0);
+      gdk_gc_set_dashes (clist->xor_gc, 0, "\4\4", 2);
+    }
+  else
+    {
+      GTK_CLIST_UNSET_FLAG (clist, CLIST_ADD_MODE);
+      gdk_gc_set_line_attributes (clist->xor_gc, 1, GDK_LINE_SOLID, 0, 0);
+      clist->anchor_state = GTK_STATE_SELECTED;
+    }
+  gtk_clist_draw_focus (GTK_WIDGET (clist));
+}
+
+static void
+toggle_focus_row (GtkCList *clist)
+{
+  g_return_if_fail (clist != 0);
+  g_return_if_fail (GTK_IS_CLIST (clist));
+
+  if (gdk_pointer_is_grabbed ())
+    return;
+
+  switch (clist->selection_mode)
+    {
+    case  GTK_SELECTION_SINGLE:
+    case  GTK_SELECTION_MULTIPLE:
+      
+      toggle_row (clist, clist->focus_row, 0, NULL);
+      break;
+      
+    case GTK_SELECTION_EXTENDED:
+      g_list_free (clist->undo_selection);
+      g_list_free (clist->undo_unselection);
+      clist->undo_selection = NULL;
+      clist->undo_unselection = NULL;
+
+      if (GTK_CLIST_ADD_MODE (clist))
+       {
+         clist->anchor = clist->focus_row;
+         clist->drag_pos = clist->focus_row;
+         clist->undo_anchor = clist->focus_row;
+         fake_toggle_row (clist, clist->focus_row);
+         GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
+       }
+      else
+       {
+         clist->anchor = clist->focus_row;
+         clist->drag_pos = clist->focus_row;
+         clist->undo_anchor = clist->focus_row;
+         GTK_CLIST_CLASS_FW (clist)->fake_unselect_all (clist,
+                                                        clist->focus_row);
+         GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
+       }
+      break;
+      
+    default:
+      break;
+    }
+}
+
+static void
+move_focus_row (GtkCList      *clist,
+               GtkScrollType  scroll_type,
+               gfloat         position)
+{
+  GtkWidget *widget;
+
+  g_return_if_fail (clist != 0);
+  g_return_if_fail (GTK_IS_CLIST (clist));
+
+  widget = GTK_WIDGET (clist);
+
+  switch (scroll_type)
+    {
+    case GTK_SCROLL_STEP_BACKWARD:
+      if (clist->focus_row <= 0)
+       return;
+      gtk_clist_draw_focus (widget);
+      clist->focus_row--;
+      gtk_clist_draw_focus (widget);
+      break;
+    case GTK_SCROLL_STEP_FORWARD:
+      if (clist->focus_row >= clist->rows - 1)
+       return;
+      gtk_clist_draw_focus (widget);
+      clist->focus_row++;
+      gtk_clist_draw_focus (widget);
+      break;
+    case GTK_SCROLL_PAGE_BACKWARD:
+      if (clist->focus_row <= 0)
+       return;
+      gtk_clist_draw_focus (widget);
+      clist->focus_row = MAX (0, clist->focus_row -
+                             (2 * clist->clist_window_height -
+                              clist->row_height - CELL_SPACING) / 
+                             (2 * (clist->row_height + CELL_SPACING)));
+      gtk_clist_draw_focus (widget);
+      break;
+    case GTK_SCROLL_PAGE_FORWARD:
+      if (clist->focus_row >= clist->rows - 1)
+       return;
+      gtk_clist_draw_focus (widget);
+      clist->focus_row = MIN (clist->rows - 1, clist->focus_row + 
+                             (2 * clist->clist_window_height -
+                              clist->row_height - CELL_SPACING) / 
+                             (2 * (clist->row_height + CELL_SPACING)));
+      gtk_clist_draw_focus (widget);
+      break;
+    case GTK_SCROLL_JUMP:
+      if (position >= 0 && position <= 1)
+       {
+         gtk_clist_draw_focus (widget);
+         clist->focus_row = position * (clist->rows - 1);
+         gtk_clist_draw_focus (widget);
+       }
+      break;
+    default:
+      break;
+    }
+}
+
+static void
+scroll_horizontal (GtkCList      *clist,
+                  GtkScrollType  scroll_type,
+                  gfloat         position)
+{
+  gint column = 0;
+
+  g_return_if_fail (clist != 0);
+  g_return_if_fail (GTK_IS_CLIST (clist));
+
+  if (gdk_pointer_is_grabbed ())
+    return;
+
+  switch (scroll_type)
+    {
+    case GTK_SCROLL_STEP_BACKWARD:
+      column = COLUMN_FROM_XPIXEL (clist, 0);
+      if (COLUMN_LEFT_XPIXEL (clist, column) - CELL_SPACING - COLUMN_INSET >= 0
+         && column > 0)
+       column--;
+      break;
+    case GTK_SCROLL_STEP_FORWARD:
+      column =  COLUMN_FROM_XPIXEL (clist, clist->clist_window_width);
+      if (column < 0)
+       return;
+      if (COLUMN_LEFT_XPIXEL (clist, column) + clist->column[column].area.width
+         + CELL_SPACING + COLUMN_INSET - 1 <= clist->clist_window_width &&
+         column < clist->columns - 1)
+       column++;
+      break;
+    case GTK_SCROLL_PAGE_BACKWARD:
+    case GTK_SCROLL_PAGE_FORWARD:
+      return;
+    case GTK_SCROLL_JUMP:
+      if (position >= 0 && position <= 1)
+       column = position * (clist->columns - 1);
+      else
+       return;
+      break;
+    default:
+      break;
+    }
+
+  if (COLUMN_LEFT_XPIXEL (clist, column) < CELL_SPACING + COLUMN_INSET)
+    gtk_clist_moveto (clist, -1, column, 0, 0);
+  else if (COLUMN_LEFT_XPIXEL (clist, column) + CELL_SPACING + COLUMN_INSET - 1
+          + clist->column[column].area.width > clist->clist_window_width)
+    {
+      if (column == clist->columns - 1)
+       gtk_clist_moveto (clist, -1, column, 0, 0);
+      else
+       gtk_clist_moveto (clist, -1, column, 0, 1);
+    }
+}
+
+static void
+scroll_vertical (GtkCList      *clist,
+                GtkScrollType  scroll_type,
+                gfloat         position)
+{
+  g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
+
+  if (gdk_pointer_is_grabbed ())
+    return;
+
+  switch (clist->selection_mode)
+    {
+    case GTK_SELECTION_EXTENDED:
+      if (clist->anchor >= 0)
+       return;
+      if (!GTK_CLIST_ADD_MODE (clist))
+       gtk_clist_unselect_all (clist);
+
+    case GTK_SELECTION_BROWSE:
+
+      if (clist->selection_mode == GTK_SELECTION_BROWSE)
+       unselect_row (clist,clist->focus_row, -1, NULL);
+
+      move_focus_row (clist, scroll_type, position);
+
+      if (clist->selection_mode == GTK_SELECTION_EXTENDED &&
+         GTK_CLIST_ADD_MODE (clist))
+       return;
+
+      switch (gtk_clist_row_is_visible (clist, clist->focus_row))
+       {
+       case GTK_VISIBILITY_NONE:
+         select_row (clist, clist->focus_row, -1, NULL);
+         switch (scroll_type)
+           {
+           case GTK_SCROLL_STEP_BACKWARD:
+           case GTK_SCROLL_PAGE_BACKWARD:
+             gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
+             break;
+           case GTK_SCROLL_STEP_FORWARD:
+           case GTK_SCROLL_PAGE_FORWARD:
+             gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
+             break;
+           case GTK_SCROLL_JUMP:
+             gtk_clist_moveto (clist, clist->focus_row, -1, 0.5, 0);
+             break;
+           default:
+             break;
+           }
+         break;
+
+       case GTK_VISIBILITY_PARTIAL:
+         switch (scroll_type)
+           {
+           case GTK_SCROLL_STEP_BACKWARD:
+           case GTK_SCROLL_PAGE_BACKWARD:
+             gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
+             break;
+           case GTK_SCROLL_STEP_FORWARD:
+           case GTK_SCROLL_PAGE_FORWARD:
+             gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
+             break;
+           case GTK_SCROLL_JUMP:
+             gtk_clist_moveto (clist, clist->focus_row, -1, 0.5, 0);
+             break;
+           default:
+             break;
+           }
+
+       default:
+         select_row (clist, clist->focus_row, -1, NULL);
+         break;
+       }
+      break;
+
+    default:
+      move_focus_row (clist, scroll_type, position);
+
+      if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
+         clist->clist_window_height)
+       gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
+      else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
+       gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
+      break;
+    }
+}
+
+static void
+set_anchor (GtkCList *clist,
+           gboolean  add_mode,
+           gint      anchor,
+           gint      undo_anchor)
+{
+  g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
+  
+  if (clist->selection_mode != GTK_SELECTION_EXTENDED || clist->anchor >= 0)
+    return;
+
+  g_list_free (clist->undo_selection);
+  g_list_free (clist->undo_unselection);
+  clist->undo_selection = NULL;
+  clist->undo_unselection = NULL;
+
+  if (add_mode)
+    fake_toggle_row (clist, anchor);
+  else
+    {
+      GTK_CLIST_CLASS_FW (clist)->fake_unselect_all (clist, anchor);
+      clist->anchor_state = GTK_STATE_SELECTED;
+    }
+
+  clist->anchor = anchor;
+  clist->drag_pos = anchor;
+  clist->undo_anchor = undo_anchor;
+}
+
+static void
+resync_selection (GtkCList *clist,
+                 GdkEvent *event)
+{
+  gint i;
+  gint e;
+  gint row;
+  gboolean thaw = FALSE;
+  GList *list;
+  GtkCListRow *clist_row;
+
+  if (clist->anchor < 0)
+    return;
+
+  if (!GTK_CLIST_FROZEN (clist))
+    {
+      GTK_CLIST_SET_FLAG (clist, CLIST_FROZEN);
+      thaw = TRUE;
+    }
+
+  i = MIN (clist->anchor, clist->drag_pos);
+  e = MAX (clist->anchor, clist->drag_pos);
+
+  if (clist->undo_selection)
+    {
+
+      list = clist->selection;
+      clist->selection = clist->undo_selection;
+      clist->selection_end = g_list_last (clist->selection);
+      clist->undo_selection = list;
+      list = clist->selection;
+      while (list)
+       {
+         row = GPOINTER_TO_INT (list->data);
+         list = list->next;
+         if (row < i || row > e)
+           {
+             clist_row = g_list_nth (clist->row_list, row)->data;
+             clist_row->state = GTK_STATE_SELECTED;
+             unselect_row (clist, row, -1, event);
+             clist->undo_selection = g_list_prepend
+               (clist->undo_selection, GINT_TO_POINTER (row));
+           }
+       }
+    }    
+
+  for (list = g_list_nth (clist->row_list, i); i <= e; i++, list = list->next)
+    if (g_list_find (clist->selection, GINT_TO_POINTER(i)))
+      {
+       if (GTK_CLIST_ROW (list)->state == GTK_STATE_NORMAL)
+         {
+           GTK_CLIST_ROW (list)->state = GTK_STATE_SELECTED;
+           unselect_row (clist, i, -1, event);
+           clist->undo_selection = g_list_prepend (clist->undo_selection,
+                                                   GINT_TO_POINTER (i));
+         }
+      }
+    else if (GTK_CLIST_ROW (list)->state == GTK_STATE_SELECTED)
+      {
+       GTK_CLIST_ROW (list)->state = GTK_STATE_NORMAL;
+       clist->undo_unselection = g_list_prepend (clist->undo_unselection,
+                                                 GINT_TO_POINTER (i));
+      }
+
+  for (list = clist->undo_unselection; list; list = list->next)
+    gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
+                    GPOINTER_TO_INT (list->data), -1, event);
+
+  clist->anchor = -1;
+  clist->drag_pos = -1;
+
+  if (thaw)
+    GTK_CLIST_UNSET_FLAG (clist, CLIST_FROZEN);
+}
+
+static void
+update_extended_selection (GtkCList *clist,
+                          gint      row)
+{
+  gint i;
+  GList *list;
+  GdkRectangle area;
+  gint s1 = -1;
+  gint s2 = -1;
+  gint e1 = -1;
+  gint e2 = -1;
+  gint y1 = clist->clist_window_height;
+  gint y2 = clist->clist_window_height;
+  gint h1 = 0;
+  gint h2 = 0;
+  gint top;
+
+  if (row < 0)
+    row = 0;
+  if (row >= clist->rows)
+    row = clist->rows - 1;
+
+  /* extending downwards */
+  if (row > clist->drag_pos && clist->anchor <= clist->drag_pos)
+    {
+      s2 = clist->drag_pos + 1;
+      e2 = row;
+    }
+  /* extending upwards */
+  else if (row < clist->drag_pos && clist->anchor >= clist->drag_pos)
+    {
+      s2 = row;
+      e2 = clist->drag_pos - 1;
+    }
+  else if (row < clist->drag_pos && clist->anchor < clist->drag_pos)
+    {
+      e1 = clist->drag_pos;
+      /* row and drag_pos on different sides of anchor :
+        take back the selection between anchor and drag_pos,
+         select between anchor and row */
+      if (row < clist->anchor)
+       {
+         s1 = clist->anchor + 1;
+         s2 = row;
+         e2 = clist->anchor - 1;
+       }
+      /* take back the selection between anchor and drag_pos */
+      else
+       s1 = row + 1;
+    }
+  else if (row > clist->drag_pos && clist->anchor > clist->drag_pos)
+    {
+      s1 = clist->drag_pos;
+      /* row and drag_pos on different sides of anchor :
+        take back the selection between anchor and drag_pos,
+         select between anchor and row */
+      if (row > clist->anchor)
+       {
+         e1 = clist->anchor - 1;
+         s2 = clist->anchor + 1;
+         e2 = row;
+       }
+      /* take back the selection between anchor and drag_pos */
+      else
+       e1 = row - 1;
+    }
+
+  clist->drag_pos = row;
+
+  area.x = 0;
+  area.width = clist->clist_window_width;
+
+  /* restore the elements between s1 and e1 */
+  if (s1 >= 0)
+    {
+      for (i = s1, list = g_list_nth (clist->row_list, i); i <= e1;
+          i++, list = list->next)
+       {
+         if (GTK_CLIST_CLASS_FW (clist)->selection_find (clist, i, list))
+            GTK_CLIST_ROW (list)->state = GTK_STATE_SELECTED;
+          else
+            GTK_CLIST_ROW (list)->state = GTK_STATE_NORMAL;
+       }
+
+      top = ROW_TOP_YPIXEL (clist, clist->focus_row);
+
+      if (top + clist->row_height <= 0)
+       {
+         area.y = 0;
+         area.height = ROW_TOP_YPIXEL (clist, e1) + clist->row_height;
+         draw_rows (clist, &area);
+         gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
+       }
+      else if (top >= clist->clist_window_height)
+       {
+         area.y = ROW_TOP_YPIXEL (clist, s1);
+         area.height = clist->clist_window_height - area.y;
+         draw_rows (clist, &area);
+         gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
+       }
+      else if (top < 0)
+       gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
+      else if (top + clist->row_height > clist->clist_window_height)
+       gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
+
+      y1 = ROW_TOP_YPIXEL (clist, s1);
+      h1 = (e1 - s1 + 1) * (clist->row_height + CELL_SPACING);
+    }
+
+  /* extend the selection between s2 and e2 */
+  if (s2 >= 0)
+    {
+      for (i = s2, list = g_list_nth (clist->row_list, i); i <= e2;
+          i++, list = list->next)
+       if (GTK_CLIST_ROW (list)->state != clist->anchor_state)
+         GTK_CLIST_ROW (list)->state = clist->anchor_state;
+
+      top = ROW_TOP_YPIXEL (clist, clist->focus_row);
+
+      if (top + clist->row_height <= 0)
+       {
+         area.y = 0;
+         area.height = ROW_TOP_YPIXEL (clist, e2) + clist->row_height;
+         draw_rows (clist, &area);
+         gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
+       }
+      else if (top >= clist->clist_window_height)
+       {
+         area.y = ROW_TOP_YPIXEL (clist, s2);
+         area.height = clist->clist_window_height - area.y;
+         draw_rows (clist, &area);
+         gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
+       }
+      else if (top < 0)
+       gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
+      else if (top + clist->row_height > clist->clist_window_height)
+       gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
+
+      y2 = ROW_TOP_YPIXEL (clist, s2);
+      h2 = (e2-s2+1) * (clist->row_height + CELL_SPACING);
+    }
+
+  area.y = MAX (0, MIN (y1, y2));
+  if (area.y > clist->clist_window_height)
+    area.y = 0;
+  area.height = MIN (clist->clist_window_height, h1 + h2);
+  if (s1 >= 0 && s2 >= 0)
+    area.height += (clist->row_height + CELL_SPACING);
+  draw_rows (clist, &area);
+}
+
+static void
+start_selection (GtkCList *clist)
+{
+  g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
+
+  if (gdk_pointer_is_grabbed ())
+    return;
+
+  set_anchor (clist, GTK_CLIST_ADD_MODE (clist), clist->focus_row,
+             clist->focus_row);
+}
+
+static void
+end_selection (GtkCList *clist)
+{
+  g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
+
+  if (gdk_pointer_is_grabbed () || clist->anchor == -1)
+    return;
+  
+  GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
+}
+
+static void
+extend_selection (GtkCList      *clist,
+                 GtkScrollType  scroll_type,
+                 gfloat         position,
+                 gboolean       auto_start_selection)
+{
+  g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
+
+  if (gdk_pointer_is_grabbed () ||
+      clist->selection_mode != GTK_SELECTION_EXTENDED)
+    return;
+
+  if (auto_start_selection)
+    set_anchor (clist, GTK_CLIST_ADD_MODE (clist), clist->focus_row,
+               clist->focus_row);
+  else if (clist->anchor == -1)
+    return;
+
+  move_focus_row (clist, scroll_type, position);
+
+  if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
+      clist->clist_window_height)
+    gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
+  else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
+    gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
+
+  update_extended_selection (clist, clist->focus_row);
+}
+
+static void
+abort_column_resize (GtkCList *clist)
+{
+  g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
+
+  if (!GTK_CLIST_IN_DRAG (clist))
+    return;
+
+  GTK_CLIST_UNSET_FLAG (clist, CLIST_IN_DRAG);
+  gdk_pointer_ungrab (gdk_time_get());
+
+  if (clist->x_drag >= 0 && clist->x_drag <= clist->clist_window_width - 1)
+    draw_xor_line (clist);
+
+  if (GTK_CLIST_ADD_MODE (clist))
+    {
+      gdk_gc_set_line_attributes (clist->xor_gc, 1, GDK_LINE_ON_OFF_DASH, 0,0);
+      gdk_gc_set_dashes (clist->xor_gc, 0, "\4\4", 2);
+    }
+}
+
+static gint
+gtk_clist_key_press (GtkWidget   * widget,
+                    GdkEventKey * event)
+{
+  GtkCList *clist;
+  gboolean handled = FALSE;
+
+  g_return_val_if_fail (widget != NULL, FALSE);
+  g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
+  g_return_val_if_fail (event != NULL, FALSE);
+
+  clist = GTK_CLIST (widget);
+
+
+  if (event->keyval == GDK_Escape && GTK_CLIST_IN_DRAG (clist))
+    {
+      GTK_CLIST_UNSET_FLAG (clist, CLIST_IN_DRAG);
+      gdk_pointer_ungrab (event->time);
+
+      if (clist->x_drag >= 0 && clist->x_drag <= clist->clist_window_width - 1)
+       draw_xor_line (clist);
+
+      if (GTK_CLIST_ADD_MODE (clist))
+       {
+         gdk_gc_set_line_attributes (clist->xor_gc, 1, GDK_LINE_ON_OFF_DASH,
+                                     0, 0);
+         gdk_gc_set_dashes (clist->xor_gc, 0, "\4\4", 2);
+       }
+      return TRUE;
+    }
+
+  if (GTK_WIDGET_CLASS (parent_class)->key_press_event)
+    handled = GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
+
+  if (handled)
+    return handled;
+
+  switch (event->keyval)
+    {
+    case GDK_Tab:
+    case GDK_ISO_Left_Tab:
+      if (event->state & GDK_SHIFT_MASK)
+       return gtk_container_focus (GTK_CONTAINER (widget),
+                                   GTK_DIR_TAB_BACKWARD);
+      else
+       return gtk_container_focus (GTK_CONTAINER (widget),
+                                   GTK_DIR_TAB_FORWARD);
+
+    default:
+      break;
+    }
+  
+  return FALSE;
+}
+
+static gboolean
+title_focus (GtkCList * clist,
+            gint       dir)
+{
+  GtkWidget *focus_child;
+  gboolean return_val = FALSE;
+  gint d = 1;
+  gint i = 0;
+  gint j;
+
+  if (!GTK_CLIST_SHOW_TITLES (clist))
+    return FALSE;
+
+  focus_child = GTK_CONTAINER (clist)->focus_child;
+  
+  switch (dir)
+    {
+    case GTK_DIR_TAB_BACKWARD:
+    case GTK_DIR_UP:
+      if (!focus_child || focus_child == clist->hscrollbar ||
+         focus_child == clist->hscrollbar ||
+         !GTK_CLIST_CHILD_HAS_FOCUS (clist))
+       {
+         if (dir == GTK_DIR_UP)
+           i = COLUMN_FROM_XPIXEL (clist, 0);
+         else
+           i = clist->columns - 1;
+         focus_child = clist->column[i].button;
+         dir = GTK_DIR_TAB_FORWARD;
+       }
+      else
+       d = -1;
+      break;
+    case GTK_DIR_LEFT:
+      d = -1;
+      if (!focus_child || focus_child == clist->hscrollbar ||
+         focus_child == clist->hscrollbar)
+       {
+         i = clist->columns - 1;
+         focus_child = clist->column[i].button;
+       }
+      break;
+    case GTK_DIR_RIGHT:
+      if (!focus_child || focus_child == clist->hscrollbar ||
+         focus_child == clist->hscrollbar)
+       {
+         i = 0;
+         focus_child = clist->column[i].button;
+       }
+      break;
+    }
+
+  if (focus_child)
+    while (i < clist->columns)
+      {
+       if (clist->column[i].button == focus_child)
+         {
+           if (clist->column[i].button && 
+               GTK_WIDGET_VISIBLE (clist->column[i].button) &&
+               GTK_IS_CONTAINER (clist->column[i].button) &&
+               !GTK_WIDGET_HAS_FOCUS (clist->column[i].button))
+             if (gtk_container_focus 
+                 (GTK_CONTAINER (clist->column[i].button), dir))
+               {
+                 return_val = TRUE;
+                 i -= d;
+               }
+           if (!return_val && dir == GTK_DIR_UP)
+             return FALSE;
+           i += d;
+           break;
+         }
+       i++;
+      }
+
+  j = i;
+
+  if (!return_val)
+    while (j >= 0 && j < clist->columns)
+      {
+       if (clist->column[j].button &&
+           GTK_WIDGET_VISIBLE (clist->column[j].button))
+         {
+           if (GTK_IS_CONTAINER (clist->column[j].button) &&
+               gtk_container_focus 
+               (GTK_CONTAINER (clist->column[j].button), dir))
+             {
+               return_val = TRUE;
+               break;
+             }
+           else if (GTK_WIDGET_CAN_FOCUS (clist->column[j].button))
+             {
+               gtk_widget_grab_focus (clist->column[j].button);
+               return_val = TRUE;
+               break;
+             }
+         }
+       j += d;
+      }
+  
+  if (return_val)
+    {
+      if (COLUMN_LEFT_XPIXEL (clist, j) < CELL_SPACING + COLUMN_INSET)
+       gtk_clist_moveto (clist, -1, j, 0, 0);
+      else if (COLUMN_LEFT_XPIXEL(clist, j) + clist->column[j].area.width >
+              clist->clist_window_width)
+       {
+         if (j == clist->columns-1)
+           gtk_clist_moveto (clist, -1, j, 0, 0);
+         else
+           gtk_clist_moveto (clist, -1, j, 0, 1);
+       }
+    }
+  return return_val;
+}
+
+static gint
+gtk_clist_focus (GtkContainer     * container,
+                GtkDirectionType   direction)
+{
+  GtkCList *clist;
+  GtkWidget *focus_child;
+  gint old_row;
+
+  g_return_val_if_fail (container != NULL, FALSE);
+  g_return_val_if_fail (GTK_IS_CLIST (container), FALSE);
+
+  if (!GTK_WIDGET_SENSITIVE (container))
+    return FALSE;
+  
+  clist = GTK_CLIST (container);
+  focus_child = container->focus_child;
+  old_row = clist->focus_row;
+
+  switch (direction)
+    {
+    case GTK_DIR_LEFT:
+    case GTK_DIR_RIGHT:
+      if (GTK_CLIST_CHILD_HAS_FOCUS (clist) &&
+         (!focus_child || (focus_child && focus_child != clist->vscrollbar && 
+                           focus_child != clist->hscrollbar)))
+       {
+         if (title_focus (clist, direction))
+           return TRUE;
+         gtk_container_set_focus_child (container, NULL);
+         return FALSE;
+        }
+      gtk_widget_grab_focus (GTK_WIDGET (container));
+      return TRUE;
+    case GTK_DIR_DOWN:
+    case GTK_DIR_TAB_FORWARD:
+      if (GTK_CLIST_CHILD_HAS_FOCUS (clist) && 
+         (!focus_child || (focus_child != clist->vscrollbar &&
+                           focus_child != clist->hscrollbar))) 
+       {
+         gboolean tf = FALSE;
+
+         if (((focus_child && direction == GTK_DIR_DOWN) ||
+              !(tf = title_focus (clist, GTK_DIR_TAB_FORWARD)))
+             && clist->rows)
+           {
+             if (clist->focus_row < 0)
+               {
+                 clist->focus_row = 0;
+
+                 if ((clist->selection_mode == GTK_SELECTION_BROWSE ||
+                      clist->selection_mode == GTK_SELECTION_EXTENDED) &&
+                     !clist->selection)
+                   select_row (clist, clist->focus_row, -1, NULL);
+               }
+             gtk_widget_grab_focus (GTK_WIDGET (container));
+             return TRUE;
+           }
+
+         if (tf)
+           return TRUE;
+       }
+      
+      GTK_CLIST_SET_FLAG (clist, CLIST_CHILD_HAS_FOCUS);
+
+      if ((!GTK_CLIST_CHILD_HAS_FOCUS (clist) || !focus_child ||
+          (focus_child != clist->vscrollbar &&
+           focus_child != clist->hscrollbar)) &&
+         GTK_WIDGET_VISIBLE (clist->vscrollbar) &&
+         GTK_WIDGET_CAN_FOCUS (clist->vscrollbar))
+       {
+         gtk_widget_grab_focus (clist->vscrollbar);
+         return TRUE;
+       }
+
+      if ((!GTK_CLIST_CHILD_HAS_FOCUS (clist) || !focus_child || 
+          focus_child != clist->hscrollbar) &&
+         GTK_WIDGET_VISIBLE (clist->hscrollbar) &&
+         GTK_WIDGET_CAN_FOCUS (clist->hscrollbar))
+       {
+         gtk_widget_grab_focus (clist->hscrollbar);
+         return TRUE;
+       }
+      break;
+    case GTK_DIR_UP:
+    case GTK_DIR_TAB_BACKWARD:
+      if (!focus_child && GTK_CLIST_CHILD_HAS_FOCUS (clist) &&
+         GTK_WIDGET_VISIBLE (clist->hscrollbar) &&
+         GTK_WIDGET_CAN_FOCUS (clist->hscrollbar))
+       {
+         gtk_widget_grab_focus (clist->hscrollbar);
+         return TRUE;
+       }
+       
+      if ((!focus_child || focus_child == clist->hscrollbar) &&
+         GTK_CLIST_CHILD_HAS_FOCUS (clist) &&
+         GTK_WIDGET_VISIBLE (clist->vscrollbar) &&
+         GTK_WIDGET_CAN_FOCUS (clist->vscrollbar))
+       {
+         gtk_widget_grab_focus (clist->vscrollbar);
+         return TRUE;
+       }
+
+      if ((!focus_child || focus_child == clist->hscrollbar ||
+          focus_child == clist->vscrollbar) &&
+         GTK_CLIST_CHILD_HAS_FOCUS (clist) && clist->rows)
+       {
+         if (clist->focus_row < 0)
+           {
+             clist->focus_row = 0;
+             if ((clist->selection_mode == GTK_SELECTION_BROWSE ||
+                  clist->selection_mode == GTK_SELECTION_EXTENDED) &&
+                 !clist->selection)
+               select_row (clist, clist->focus_row, -1, NULL);
+           }
+         gtk_widget_grab_focus (GTK_WIDGET (container));
+         return TRUE;
+       }
+
+      GTK_CLIST_SET_FLAG (clist, CLIST_CHILD_HAS_FOCUS);
+
+      if (title_focus (clist, direction))
+       return TRUE;
+
+      break;
+
+    default:
+      break;
+    }
+
+  gtk_container_set_focus_child (container, NULL);
+  return FALSE;
+}
+
+void
+gtk_clist_unselect_all (GtkCList * clist)
+{
+  GTK_CLIST_CLASS_FW (clist)->unselect_all (clist);
+}
+
+static void
+real_unselect_all (GtkCList * clist)
+{
+  GList *list;
+  gint i;
+  g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
+
+  if (gdk_pointer_is_grabbed ())
+    return;
+
+  switch (clist->selection_mode)
+    {
+    case GTK_SELECTION_BROWSE:
+      if (clist->focus_row >= 0)
+       {
+         select_row (clist, clist->focus_row, -1, NULL);
+         return;
+       }
+      break;
+
+    case GTK_SELECTION_EXTENDED:
+      g_list_free (clist->undo_selection);
+      g_list_free (clist->undo_unselection);
+      clist->undo_selection = NULL;
+      clist->undo_unselection = NULL;
+
+      clist->anchor = -1;
+      clist->drag_pos = -1;
+      clist->undo_anchor = clist->focus_row;
+      break;
+
+    default:
+      break;
+    }
+
+  list = clist->selection;
+
+  while (list)
+    {
+      i = GPOINTER_TO_INT (list->data);
+      list = list->next;
+      unselect_row (clist, i, -1, NULL);
+    }
+}
+
+void
+gtk_clist_select_all (GtkCList * clist)
+{
+  GTK_CLIST_CLASS_FW (clist)->select_all (clist);
+}
+
+static void
+real_select_all (GtkCList * clist)
+{
+  GList *list;
+  gint i;
+  g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
+
+  if (gdk_pointer_is_grabbed ())
+    return;
+
+  switch (clist->selection_mode)
+    {
+    case GTK_SELECTION_SINGLE:
+    case GTK_SELECTION_BROWSE:
+      return;
+
+    case GTK_SELECTION_EXTENDED:
+      g_list_free (clist->undo_selection);
+      g_list_free (clist->undo_unselection);
+      clist->undo_selection = NULL;
+      clist->undo_unselection = NULL;
+         
+      if (clist->rows &&
+         ((GtkCListRow *) (clist->row_list->data))->state !=
+         GTK_STATE_SELECTED)
+       fake_toggle_row (clist, 0);
+
+      clist->anchor_state =  GTK_STATE_SELECTED;
+      clist->anchor = 0;
+      clist->drag_pos = 0;
+      clist->undo_anchor = clist->focus_row;
+      update_extended_selection (clist, clist->rows);
+      GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
+      return;
+
+    case GTK_SELECTION_MULTIPLE:
+      for (i = 0, list = clist->row_list; list; i++, list = list->next)
+       {
+         if (((GtkCListRow *)(list->data))->state == GTK_STATE_NORMAL)
+           gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
+                            i, -1, NULL);
+       }
+      return;
+    }
+}
+
+static void
+fake_unselect_all (GtkCList * clist,
+                  gint       row)
+{
+  GList *list;
+  GList *work;
+  gint i;
+
+  if (row >= 0 && (work = g_list_nth (clist->row_list, row)))
+    {
+      if (GTK_CLIST_ROW (work)->state == GTK_STATE_NORMAL)
+       {
+         GTK_CLIST_ROW (work)->state = GTK_STATE_SELECTED;
+         
+         if (!GTK_CLIST_FROZEN (clist) &&
+             gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
+           GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row,
+                                                 GTK_CLIST_ROW (work));
+       }  
+    }
+
+  clist->undo_selection = clist->selection;
+  clist->selection = NULL;
+  clist->selection_end = NULL;
+  
+  for (list = clist->undo_selection; list; list = list->next)
+    {
+      if ((i = GPOINTER_TO_INT (list->data)) == row ||
+         !(work = g_list_nth (clist->row_list, i)))
+       continue;
+
+      GTK_CLIST_ROW (work)->state = GTK_STATE_NORMAL;
+      if (!GTK_CLIST_FROZEN (clist) &&
+         gtk_clist_row_is_visible (clist, i) != GTK_VISIBILITY_NONE)
+       GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, i,
+                                             GTK_CLIST_ROW (work));
+    }
+}
+
+static void
+fake_toggle_row (GtkCList *clist,
+                gint      row)
+{
+  GList *work;
+
+  if (!(work = g_list_nth (clist->row_list, row)))
+    return;
+  
+  if (GTK_CLIST_ROW (work)->state == GTK_STATE_NORMAL)
+    clist->anchor_state = GTK_CLIST_ROW (work)->state = GTK_STATE_SELECTED;
+  else
+    clist->anchor_state = GTK_CLIST_ROW (work)->state = GTK_STATE_NORMAL;
+  
+  if (!GTK_CLIST_FROZEN (clist) &&
+      gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
+    GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row,
+                                         GTK_CLIST_ROW (work));
+}
+
+static GList *
+selection_find (GtkCList *clist,
+               gint      row_number,
+               GList    *row_list_element)
+{
+  return g_list_find (clist->selection, GINT_TO_POINTER (row_number));
+}
index fb1ed5d58daf4944c193e18e6787345a1116a737..ec347dda7439a285ca6946f48bec5cc0356c6485 100644 (file)
@@ -27,6 +27,7 @@
 #include <gtk/gtkbutton.h>
 #include <gtk/gtkhscrollbar.h>
 #include <gtk/gtkvscrollbar.h>
+#include <gtk/gtkenums.h>
 
 #ifdef __cplusplus
 extern "C"
@@ -38,9 +39,12 @@ enum
 {
   GTK_CLIST_FROZEN          = 1 << 0,                                     
   GTK_CLIST_IN_DRAG         = 1 << 1,                                        
-  GTK_CLIST_ROW_HEIGHT_SET  = 1 << 2,
-  GTK_CLIST_SHOW_TITLES     = 1 << 3,
-  GTK_CLIST_CONSTRUCTED            = 1 << 4
+  GTK_CLIST_DRAG_SELECTION  = 1 << 2,
+  GTK_CLIST_ROW_HEIGHT_SET  = 1 << 3,
+  GTK_CLIST_SHOW_TITLES     = 1 << 4,
+  GTK_CLIST_CONSTRUCTED            = 1 << 5,
+  GTK_CLIST_CHILD_HAS_FOCUS = 1 << 6,
+  GTK_CLIST_ADD_MODE        = 1 << 7
 }; 
 
 /* cell types */
@@ -53,11 +57,11 @@ typedef enum
   GTK_CELL_WIDGET
 } GtkCellType;
 
-#define GTK_TYPE_CLIST                  (gtk_clist_get_type ())
-#define GTK_CLIST(obj)                  (GTK_CHECK_CAST ((obj), GTK_TYPE_CLIST, GtkCList))
-#define GTK_CLIST_CLASS(klass)          (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_CLIST, GtkCListClass))
-#define GTK_IS_CLIST(obj)               (GTK_CHECK_TYPE ((obj), GTK_TYPE_CLIST))
-#define GTK_IS_CLIST_CLASS(klass)       (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CLIST))
+#define GTK_TYPE_CLIST                    (gtk_clist_get_type ())
+#define GTK_CLIST(obj)                    (GTK_CHECK_CAST ((obj), gtk_clist_get_type (), GtkCList))
+#define GTK_CLIST_CLASS(klass)            (GTK_CHECK_CLASS_CAST ((klass), gtk_clist_get_type (), GtkCListClass))
+#define GTK_IS_CLIST(obj)                 (GTK_CHECK_TYPE ((obj), gtk_clist_get_type ()))
+#define GTK_IS_CLIST_CLASS(klass)         (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CLIST))
 
 #define GTK_CLIST_FLAGS(clist)            (GTK_CLIST (clist)->flags)
 #define GTK_CLIST_SET_FLAG(clist,flag)    (GTK_CLIST_FLAGS (clist) |= (GTK_ ## flag))
@@ -68,6 +72,11 @@ typedef enum
 #define GTK_CLIST_ROW_HEIGHT_SET(clist)    (GTK_CLIST_FLAGS (clist) & GTK_CLIST_ROW_HEIGHT_SET)
 #define GTK_CLIST_SHOW_TITLES(clist)       (GTK_CLIST_FLAGS (clist) & GTK_CLIST_SHOW_TITLES)
 #define GTK_CLIST_CONSTRUCTED(clist)       (GTK_CLIST_FLAGS (clist) & GTK_CLIST_CONSTRUCTED)
+#define GTK_CLIST_CHILD_HAS_FOCUS(clist)   (GTK_CLIST_FLAGS (clist) & GTK_CLIST_CHILD_HAS_FOCUS)
+#define GTK_CLIST_DRAG_SELECTION(clist)    (GTK_CLIST_FLAGS (clist) & GTK_CLIST_DRAG_SELECTION)
+#define GTK_CLIST_ADD_MODE(clist)          (GTK_CLIST_FLAGS (clist) & GTK_CLIST_ADD_MODE)
+
+#define GTK_CLIST_ROW(_glist_) ((GtkCListRow *)((_glist_)->data))
 
 /* pointer casting for cells */
 #define GTK_CELL_TEXT(cell)     (((GtkCellText *) &(cell)))
@@ -90,7 +99,7 @@ struct _GtkCList
 {
   GtkContainer container;
   
-  guint8 flags;
+  guint16 flags;
 
   /* mem chunks */
   GMemChunk *row_mem_chunk;
@@ -133,6 +142,11 @@ struct _GtkCList
 
   /* list of selected rows */
   GList *selection;
+  GList *selection_end;
+
+  GList *undo_selection;
+  GList *undo_unselection;
+  gint undo_anchor;
 
   /* scrollbars */
   GtkWidget *vscrollbar;
@@ -152,6 +166,16 @@ struct _GtkCList
 
   /* the current x-pixel location of the xor-drag line */
   gint x_drag;
+
+  /* focus handling */
+  gint focus_row;
+
+  /* dragging the selection */
+  gint anchor;
+  GtkStateType anchor_state;
+  gint drag_pos;
+  gint htimer;
+  gint vtimer;
 };
 
 struct _GtkCListClass
@@ -161,17 +185,48 @@ struct _GtkCListClass
   void (*select_row) (GtkCList * clist,
                      gint row,
                      gint column,
-                     GdkEventButton * event);
+                     GdkEvent * event);
   void (*unselect_row) (GtkCList * clist,
                        gint row,
                        gint column,
-                       GdkEventButton * event);
+                       GdkEvent * event);
   void (*click_column) (GtkCList * clist,
                        gint column);
+
+  void (*toggle_focus_row) (GtkCList * clist);
+  void (*select_all) (GtkCList * clist);
+  void (*unselect_all) (GtkCList * clist);
+  void (*undo_selection) (GtkCList * clist);
+  void (*start_selection) (GtkCList * clist);
+  void (*end_selection) (GtkCList * clist);
+  void (*extend_selection) (GtkCList * clist,
+                           GtkScrollType scroll_type,
+                           gfloat position,
+                           gboolean auto_start_selection);
+  void (*scroll_horizontal) (GtkCList * clist,
+                            GtkScrollType scroll_type,
+                            gfloat position);
+  void (*scroll_vertical) (GtkCList * clist,
+                          GtkScrollType scroll_type,
+                          gfloat position);
+  void (*toggle_add_mode) (GtkCList * clist);
+  void (*abort_column_resize) (GtkCList * clist);
+
+  void (*resync_selection) (GtkCList * clist,
+                           GdkEvent * event);
+  GList * (*selection_find) (GtkCList *clist,
+                            gint row_number,
+                            GList *row_list_element);
   void (*draw_row) (GtkCList * clist,
                    GdkRectangle * area,
                    gint row,
                    GtkCListRow * clist_row);
+  void (*clear) (GtkCList * clist);
+  void (*fake_unselect_all) (GtkCList * clist,
+                            gint row);
+        
+
+
   gint scrollbar_spacing;
 };
 
@@ -364,9 +419,6 @@ void gtk_clist_moveto (GtkCList * clist,
 GtkVisibility gtk_clist_row_is_visible (GtkCList * clist,
                                        gint row);
 
-GtkAdjustment* gtk_clist_get_vadjustment (GtkCList * clist);
-GtkAdjustment* gtk_clist_get_hadjustment (GtkCList * clist);
-
 /* returns the cell type */
 GtkCellType gtk_clist_get_cell_type (GtkCList * clist,
                                     gint row,
@@ -480,6 +532,9 @@ void gtk_clist_unselect_row (GtkCList * clist,
                             gint row,
                             gint column);
 
+/* undo the last select/unselect operation */
+void gtk_clist_undo_selection (GtkCList *clist);
+
 /* clear the entire list -- this is much faster than removing each item 
  * with gtk_clist_remove */
 void gtk_clist_clear (GtkCList * clist);
@@ -491,10 +546,14 @@ gint gtk_clist_get_selection_info (GtkCList * clist,
                                   gint * row,
                                   gint * column);
 
-/* swap the position of two rows */
-void gtk_clist_swap_rows (GtkCList * clist, gint row1, gint row2);
+/* in multiple or extended mode, select all rows */
+void gtk_clist_select_all (GtkCList *clist);
 
+/* in all modes except browse mode, deselect all rows */
+void gtk_clist_unselect_all (GtkCList *clist);
 
+/* swap the position of two rows */
+void gtk_clist_swap_rows (GtkCList * clist, gint row1, gint row2);
 
 #ifdef __cplusplus
 }
index 5e001c1782242f74044729568930db12a1889381..503b163fe649fed44dc063ca7690843460cdfd57 100644 (file)
@@ -23,7 +23,9 @@
 
 #include <stdlib.h>
 #include "gtkctree.h"
+#include "gtkbindings.h"
 #include <gdk/gdkx.h>
+#include <gdk/gdkkeysyms.h>
 
 #define PM_SIZE                    8
 #define TAB_SIZE                   (PM_SIZE + 6)
@@ -41,6 +43,9 @@
                                     + (clist)->hoffset)
 #define COLUMN_LEFT(clist, column) ((clist)->column[(column)].area.x)
 
+#define GTK_CLIST_CLASS_FW(_widget_) GTK_CLIST_CLASS (GTK_OBJECT (_widget_)->klass)
+
+
 static void gtk_ctree_class_init        (GtkCTreeClass  *klass);
 static void gtk_ctree_init              (GtkCTree       *ctree);
 static void gtk_ctree_destroy           (GtkObject      *object);
@@ -102,6 +107,7 @@ static void tree_delete                 (GtkCTree      *ctree,
 static void tree_delete_row             (GtkCTree      *ctree, 
                                         GList         *node, 
                                         gpointer       data);
+static void real_clear                  (GtkCList      *clist);
 static void tree_update_level           (GtkCTree      *ctree, 
                                         GList         *node, 
                                         gpointer       data);
@@ -111,12 +117,27 @@ static void tree_select                 (GtkCTree      *ctree,
 static void tree_unselect               (GtkCTree      *ctree, 
                                         GList         *node, 
                                         gpointer       data);
+static void real_select_all             (GtkCList      *clist);
+static void real_unselect_all           (GtkCList      *clist);
 static void tree_expand                 (GtkCTree      *ctree, 
                                         GList         *node,
                                         gpointer       data);
 static void tree_collapse               (GtkCTree      *ctree, 
                                         GList         *node,
                                         gpointer       data);
+static void tree_toggle_expansion       (GtkCTree      *ctree,
+                                        GList         *node,
+                                        gpointer       data);
+static void change_focus_row_expansion  (GtkCTree      *ctree,
+                                        GtkCTreeExpansion expansion);
+static void real_select_row             (GtkCList      *clist,
+                                        gint           row,
+                                        gint           column,
+                                        GdkEvent      *event);
+static void real_unselect_row           (GtkCList      *clist,
+                                        gint           row,
+                                        gint           column,
+                                        GdkEvent      *event);
 static void real_tree_select            (GtkCTree      *ctree,
                                         GList         *node,
                                         gint           column);
@@ -137,9 +158,11 @@ static void real_tree_move              (GtkCTree      *ctree,
 static void gtk_ctree_link              (GtkCTree      *ctree,
                                         GList         *node,
                                         GList         *parent,
-                                        GList         *sibling);
+                                        GList         *sibling,
+                                        gboolean       update_focus_row);
 static void gtk_ctree_unlink            (GtkCTree      *ctree, 
-                                        GList         *node);
+                                        GList         *node,
+                                        gboolean       update_focus_row);
 static GList * gtk_ctree_last_visible    (GtkCTree     *ctree,
                                          GList        *node);
 static void gtk_ctree_marshal_signal_1  (GtkObject     *object,
@@ -154,6 +177,10 @@ static void gtk_ctree_marshal_signal_3  (GtkObject     *object,
                                         GtkSignalFunc  func,
                                         gpointer       func_data,
                                         GtkArg        *args);
+static void gtk_ctree_marshal_signal_4  (GtkObject     *object,
+                                        GtkSignalFunc  func,
+                                        gpointer       func_data,
+                                        GtkArg        *args);
 static gboolean ctree_is_hot_spot       (GtkCTree      *ctree, 
                                         GList         *node,
                                         gint           row, 
@@ -166,6 +193,23 @@ static gint default_compare             (GtkCTree      *ctree,
                                         const GList   *node1,
                                         const GList   *node2);
 
+
+
+static void fake_unselect_all           (GtkCList      *clist,
+                                        gint           row);
+static GList*  selection_find           (GtkCList      *clist,
+                                        gint           row_number,
+                                        GList         *row_list_element);
+static void resync_selection            (GtkCList      *clist,
+                                        GdkEvent      *event);
+static void real_undo_selection         (GtkCList      *clist);
+static void select_row_recursive        (GtkCTree      *ctree, 
+                                        GList         *node, 
+                                        gpointer       data);
+
+
+
+
 enum
 {
   TREE_SELECT_ROW,
@@ -173,6 +217,7 @@ enum
   TREE_EXPAND,
   TREE_COLLAPSE,
   TREE_MOVE,
+  CHANGE_FOCUS_ROW_EXPANSION,
   LAST_SIGNAL
 };
 
@@ -191,6 +236,10 @@ typedef void (*GtkCTreeSignal3) (GtkObject *object,
                                 GList     *arg1,
                                 gpointer   data);
 
+typedef void (*GtkCTreeSignal4) (GtkObject         *object,
+                                GtkCTreeExpansion  arg1,
+                                gpointer           data);
+
 
 static GtkCListClass *parent_class = NULL;
 static GtkContainerClass *container_class = NULL;
@@ -263,6 +312,19 @@ gtk_ctree_marshal_signal_3 (GtkObject     *object,
   (*rfunc) (object, GTK_VALUE_POINTER (args[0]), func_data);
 }
 
+static void
+gtk_ctree_marshal_signal_4 (GtkObject     *object,
+                           GtkSignalFunc  func,
+                           gpointer       func_data,
+                           GtkArg        *args)
+{
+  GtkCTreeSignal4 rfunc;
+
+  rfunc = (GtkCTreeSignal4) func;
+
+  (*rfunc) (object, GTK_VALUE_ENUM (args[0]), func_data);
+}
+
 static void
 gtk_ctree_class_init (GtkCTreeClass *klass)
 {
@@ -314,6 +376,14 @@ gtk_ctree_class_init (GtkCTreeClass *klass)
                    gtk_ctree_marshal_signal_2,
                    GTK_TYPE_NONE, 3, GTK_TYPE_POINTER, GTK_TYPE_POINTER, 
                    GTK_TYPE_POINTER);
+  ctree_signals[CHANGE_FOCUS_ROW_EXPANSION] =
+    gtk_signal_new ("change_focus_row_expansion",
+                   GTK_RUN_LAST | GTK_RUN_ACTION,
+                   object_class->type,
+                   GTK_SIGNAL_OFFSET (GtkCTreeClass,
+                                      change_focus_row_expansion),
+                   gtk_ctree_marshal_signal_4,
+                   GTK_TYPE_NONE, 1, GTK_TYPE_ENUM);
 
   gtk_object_class_add_signals (object_class, ctree_signals, LAST_SIGNAL);
 
@@ -325,16 +395,70 @@ gtk_ctree_class_init (GtkCTreeClass *klass)
   widget_class->button_release_event = gtk_ctree_button_release;
   widget_class->motion_notify_event = gtk_ctree_button_motion;
 
-  clist_class->select_row = NULL;
-  clist_class->unselect_row = NULL;
+  clist_class->select_row = real_select_row;
+  clist_class->unselect_row = real_unselect_row;
+  clist_class->undo_selection = real_undo_selection;
+  clist_class->resync_selection = resync_selection;
+  clist_class->selection_find = selection_find;
   clist_class->click_column = NULL;
   clist_class->draw_row = draw_row;
+  clist_class->clear = real_clear;
+  clist_class->select_all = real_select_all;
+  clist_class->unselect_all = real_unselect_all;
+  clist_class->fake_unselect_all = fake_unselect_all;
 
   klass->tree_select_row = real_tree_select;
   klass->tree_unselect_row = real_tree_unselect;
   klass->tree_expand = real_tree_expand;
   klass->tree_collapse = real_tree_collapse;
   klass->tree_move = real_tree_move;
+  klass->change_focus_row_expansion = change_focus_row_expansion;
+
+  {
+    GtkBindingSet *binding_set;
+
+    binding_set = gtk_binding_set_by_class (klass);
+    gtk_binding_entry_add_signal (binding_set,
+                                 '+', GDK_SHIFT_MASK,
+                                 "change_focus_row_expansion", 1,
+                                 GTK_TYPE_ENUM, GTK_CTREE_EXPANSION_EXPAND);
+    gtk_binding_entry_add_signal (binding_set,
+                                 GDK_KP_Add, 0,
+                                 "change_focus_row_expansion", 1,
+                                 GTK_TYPE_ENUM, GTK_CTREE_EXPANSION_EXPAND);
+    gtk_binding_entry_add_signal (binding_set,
+                                 GDK_KP_Add, GDK_CONTROL_MASK,
+                                 "change_focus_row_expansion", 1,
+                                 GTK_TYPE_ENUM,
+                                 GTK_CTREE_EXPANSION_EXPAND_RECURSIVE);
+    gtk_binding_entry_add_signal (binding_set,
+                                 '-', 0,
+                                 "change_focus_row_expansion", 1,
+                                 GTK_TYPE_ENUM, GTK_CTREE_EXPANSION_COLLAPSE);
+    gtk_binding_entry_add_signal (binding_set,
+                                 GDK_KP_Subtract, 0,
+                                 "change_focus_row_expansion", 1,
+                                 GTK_TYPE_ENUM, GTK_CTREE_EXPANSION_COLLAPSE);
+    gtk_binding_entry_add_signal (binding_set,
+                                 GDK_KP_Subtract, GDK_CONTROL_MASK,
+                                 "change_focus_row_expansion", 1,
+                                 GTK_TYPE_ENUM,
+                                 GTK_CTREE_EXPANSION_COLLAPSE_RECURSIVE);
+    gtk_binding_entry_add_signal (binding_set,
+                                 '=', 0,
+                                 "change_focus_row_expansion", 1,
+                                 GTK_TYPE_ENUM, GTK_CTREE_EXPANSION_TOGGLE);
+    gtk_binding_entry_add_signal (binding_set,
+                                 GDK_KP_Multiply, 0,
+                                 "change_focus_row_expansion", 1,
+                                 GTK_TYPE_ENUM, GTK_CTREE_EXPANSION_TOGGLE);
+    gtk_binding_entry_add_signal (binding_set,
+                                 GDK_KP_Multiply, GDK_CONTROL_MASK,
+                                 "change_focus_row_expansion", 1,
+                                 GTK_TYPE_ENUM,
+                                 GTK_CTREE_EXPANSION_TOGGLE_RECURSIVE);
+  }
+
 }
 
 static void
@@ -344,7 +468,6 @@ gtk_ctree_init (GtkCTree *ctree)
   ctree->drag_icon      = NULL;
   ctree->tree_indent    = 20;
   ctree->tree_column    = 0;
-  ctree->selection_last = NULL;
   ctree->drag_row       = -1;
   ctree->drag_source    = NULL;
   ctree->drag_target    = NULL;
@@ -371,7 +494,7 @@ gtk_ctree_destroy (GtkObject *object)
 
   GTK_CLIST_SET_FLAG (clist, CLIST_FROZEN);
 
-  gtk_ctree_clear (GTK_CTREE (object));
+  gtk_clist_clear (GTK_CLIST (object));
 
   if (clist->vscrollbar)
     {
@@ -458,6 +581,7 @@ gtk_ctree_button_press (GtkWidget      *widget,
 
   if (event->window == clist->clist_window)
     {
+      gboolean collapse_expand = FALSE;
       GList *work;
       gint x;
       gint y;
@@ -466,40 +590,105 @@ gtk_ctree_button_press (GtkWidget      *widget,
 
       x = event->x;
       y = event->y;
-      if (gtk_clist_get_selection_info (clist, x, y, &row, &column))
-       {
-         if (event->button == 1)
-           ctree->drag_row = - 1 - ROW_FROM_YPIXEL (clist, y);
 
-         work = g_list_nth (clist->row_list, row);
+      if (!gtk_clist_get_selection_info (clist, x, y, &row, &column))
+       return FALSE;
+
+      if (event->button == 2)
+       ctree->drag_row = - 1 - ROW_FROM_YPIXEL (clist, y);
+
+      work = g_list_nth (clist->row_list, row);
          
-         if (GTK_CTREE_ROW (work)->children &&
-             (event->type == GDK_2BUTTON_PRESS ||
-              (ctree_is_hot_spot (ctree, work, row, x, y)
-               && event->button == 1)))
-           {
-             if (GTK_CTREE_ROW (work)->expanded)
-               gtk_ctree_collapse (ctree, work);
-             else
-               gtk_ctree_expand (ctree, work);
-           }
-         else if (ctree->reorderable && event->button == 1 && !ctree->in_drag)
+      if (ctree->reorderable && event->button == 2 && !ctree->in_drag &&
+         clist->anchor == -1)
+       {
+         gdk_pointer_grab (event->window, FALSE,
+                           GDK_POINTER_MOTION_HINT_MASK |
+                           GDK_BUTTON2_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
+                           NULL, NULL, event->time);
+         ctree->in_drag = TRUE;
+         ctree->drag_source = work;
+         ctree->drag_target = NULL;
+         return FALSE;
+       }
+      else if (event->button == 1 &&
+              (GTK_CTREE_ROW (work)->children &&
+               (event->type == GDK_2BUTTON_PRESS ||
+                ctree_is_hot_spot (ctree, work, row, x, y))))
+       {
+         if (GTK_CTREE_ROW (work)->expanded)
+           gtk_ctree_collapse (ctree, work);
+         else
+           gtk_ctree_expand (ctree, work);
+
+         collapse_expand = TRUE;
+       }
+      if (event->button == 1)
+       {
+         gint old_row = clist->focus_row;
+         gboolean no_focus_row = FALSE;
+
+         switch (clist->selection_mode)
            {
-             gdk_pointer_grab (event->window, FALSE,
+           case GTK_SELECTION_MULTIPLE:
+           case GTK_SELECTION_SINGLE:
+             if (!collapse_expand)
+               break;
+
+             if (clist->focus_row == -1)
+               {
+                 old_row = row;
+                 no_focus_row = TRUE;
+               }
+                 
+             GTK_CLIST_SET_FLAG (clist, CLIST_DRAG_SELECTION);
+             gdk_pointer_grab (clist->clist_window, FALSE,
                                GDK_POINTER_MOTION_HINT_MASK |
                                GDK_BUTTON1_MOTION_MASK |
                                GDK_BUTTON_RELEASE_MASK,
                                NULL, NULL, event->time);
-             ctree->in_drag = TRUE;
-             ctree->drag_source = work;
-             ctree->drag_target = NULL;
+
+             if (GTK_CLIST_ADD_MODE (clist))
+               {
+                 GTK_CLIST_UNSET_FLAG (clist, CLIST_ADD_MODE);
+                 if (GTK_WIDGET_HAS_FOCUS (widget))
+                   {
+                     gtk_widget_draw_focus (widget);
+                     gdk_gc_set_line_attributes (clist->xor_gc, 1,
+                                                 GDK_LINE_SOLID, 0, 0);
+                     clist->focus_row = row;
+                     gtk_widget_draw_focus (widget);
+                   }
+                 else
+                   {
+                     gdk_gc_set_line_attributes (clist->xor_gc, 1,
+                                                 GDK_LINE_SOLID, 0, 0);
+                     clist->focus_row = row;
+                   }
+               }
+             else if (row != clist->focus_row)
+               {
+                 if (GTK_WIDGET_HAS_FOCUS (widget))
+                   {
+                     gtk_widget_draw_focus (widget);
+                     clist->focus_row = row;
+                     gtk_widget_draw_focus (widget);
+                   }
+                 else
+                   clist->focus_row = row;
+               }
+
+             if (!GTK_WIDGET_HAS_FOCUS (widget))
+               gtk_widget_grab_focus (widget);
+
              return FALSE;
+
+           default:
+             break;
            }
        }
-      return FALSE;
     }
-  return 
-    (* GTK_WIDGET_CLASS (parent_class)->button_press_event) (widget, event);
+  return GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
 }
 
 static gint
@@ -520,11 +709,12 @@ gtk_ctree_button_motion (GtkWidget      *widget,
   ctree = GTK_CTREE (widget);
   clist = GTK_CLIST (widget);
 
-  if (!ctree->reorderable)
-    return 
-      (* GTK_WIDGET_CLASS (parent_class)->motion_notify_event) (widget, event);
+  if (GTK_CLIST_IN_DRAG (clist))
+    return  GTK_WIDGET_CLASS (parent_class)->motion_notify_event
+      (widget, event);
 
-  if (event->window == clist->clist_window && ctree->in_drag)
+  if (event->window == clist->clist_window && 
+      ctree->in_drag && ctree->reorderable)
     {
       GdkModifierType modmask;
       gint root_x;
@@ -540,8 +730,7 @@ gtk_ctree_button_motion (GtkWidget      *widget,
          y >= ROW_TOP_YPIXEL (clist, -ctree->drag_row-1) &&
          y <= ROW_TOP_YPIXEL (clist, -ctree->drag_row-1) + clist->row_height)
        return 
-         (* GTK_WIDGET_CLASS (parent_class)->motion_notify_event) 
-         (widget, event);
+         GTK_WIDGET_CLASS (parent_class)->motion_notify_event (widget, event);
 
       if (ctree->use_icons)
        {
@@ -585,7 +774,7 @@ gtk_ctree_button_motion (GtkWidget      *widget,
       if (y < 0 || y > clist->clist_window_height || 
          ROW_TOP_YPIXEL (clist, row + 1) > clist->clist_window_height
          || row >= clist->rows)
-       return (* GTK_WIDGET_CLASS (parent_class)->motion_notify_event) 
+       return GTK_WIDGET_CLASS (parent_class)->motion_notify_event
          (widget, event);
 
       if (y - ROW_TOP_YPIXEL (clist, row) < clist->row_height / 4)
@@ -632,8 +821,7 @@ gtk_ctree_button_motion (GtkWidget      *widget,
            }
        }
     }
-  return 
-    (* GTK_WIDGET_CLASS (parent_class)->motion_notify_event) (widget, event);
+  return GTK_WIDGET_CLASS (parent_class)->motion_notify_event (widget, event);
 }
 
 static gint
@@ -650,7 +838,7 @@ gtk_ctree_button_release (GtkWidget      *widget,
   ctree = GTK_CTREE (widget);
   clist = GTK_CLIST (widget);
 
-  if (event->button == 1)
+  if (event->button == 2 && clist->anchor == -1)
     {
       gdk_pointer_ungrab (event->time);
       ctree->in_drag = FALSE;
@@ -672,40 +860,13 @@ gtk_ctree_button_release (GtkWidget      *widget,
            draw_xor_line (ctree);
          ctree->drag_row = -1;
        }
-      else
-       {
-         if (event->window == clist->clist_window)
-           {
-             gint row;
-             gint column;
-             GList *work;
-
-             if (gtk_clist_get_selection_info (clist, event->x, event->y, 
-                                               &row, &column))
-               if (row == - (ctree->drag_row + 1) && 
-                   (work = g_list_nth (clist->row_list, row)))
-                 {
-                   if (GTK_CTREE_ROW (work)->children && 
-                       ctree_is_hot_spot (ctree, work, row, event->x,
-                                          event->y))
-                     return (* GTK_WIDGET_CLASS (parent_class)
-                             ->button_release_event) (widget, event);
-                   tree_toggle_selection (ctree, work, column);
-                 }
-             return FALSE;
-           }
-         return 
-           (* GTK_WIDGET_CLASS (parent_class)->button_release_event) 
-           (widget, event);
-       }
 
       /* nop if out of bounds / source == target */
       if (event->x < 0 || event->y < -3 ||
          event->x > clist->clist_window_width ||
          event->y > clist->clist_window_height + 3 ||
          ctree->drag_target == ctree->drag_source)
-       return
-         (* GTK_WIDGET_CLASS (parent_class)->button_release_event) 
+       return GTK_WIDGET_CLASS (parent_class)->button_release_event
          (widget, event);
 
       if (!GTK_CTREE_ROW (ctree->drag_source)->children ||
@@ -746,8 +907,24 @@ gtk_ctree_button_release (GtkWidget      *widget,
       ctree->drag_source = NULL;
       ctree->drag_target = NULL;
     }
-  return 
-     (* GTK_WIDGET_CLASS (parent_class)->button_release_event) (widget, event);
+  else if (event->button == 1 && GTK_CLIST_DRAG_SELECTION (clist) &&
+          (clist->selection_mode == GTK_SELECTION_SINGLE ||
+           clist->selection_mode == GTK_SELECTION_MULTIPLE))
+    {
+      gint row;
+      gint column;
+      GList *work;
+
+      if (gtk_clist_get_selection_info
+         (clist, event->x, event->y, &row, &column))
+       {
+         if (clist->anchor == clist->focus_row &&
+             (work = g_list_nth (clist->row_list, row)))
+           tree_toggle_selection (ctree, work, column);
+       }
+      clist->anchor = -1;
+    }
+  return GTK_WIDGET_CLASS (parent_class)->button_release_event (widget, event);
 }
 
 static void
@@ -2246,6 +2423,26 @@ draw_row (GtkCList     *clist,
            }
        }
     }
+  if (clist->focus_row == row && GTK_WIDGET_HAS_FOCUS (widget))
+    {
+      if (area)
+       {
+         if (gdk_rectangle_intersect (area, &row_rectangle,
+                                      &intersect_rectangle))
+           {
+             gdk_gc_set_clip_rectangle (clist->xor_gc, &intersect_rectangle);
+             gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
+                                 row_rectangle.x, row_rectangle.y,
+                                 row_rectangle.width - 1,
+                                 row_rectangle.height - 1);
+             gdk_gc_set_clip_rectangle (clist->xor_gc, NULL);
+           }
+       }
+      else
+       gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
+                           row_rectangle.x, row_rectangle.y,
+                           row_rectangle.width - 1, row_rectangle.height - 1);
+    }
 }
 
 static void
@@ -2268,8 +2465,8 @@ tree_draw_row (GtkCTree *ctree,
          num++;
        }
       if (gtk_clist_row_is_visible (clist, num) != GTK_VISIBILITY_NONE)
-       (GTK_CLIST_CLASS (GTK_OBJECT (clist)->klass)->draw_row) 
-         (clist, NULL, num, &(GTK_CTREE_ROW (row)->row));
+       GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, num,
+                                             GTK_CLIST_ROW (row));
     }
 }
 
@@ -2297,11 +2494,12 @@ static void
 gtk_ctree_link (GtkCTree *ctree,
                GList    *node,
                GList    *parent,
-               GList    *sibling)
+               GList    *sibling,
+               gboolean  update_focus_row)
 {
   GtkCList *clist;
   GList *list_end;
-  gint visible = TRUE;
+  gboolean visible = FALSE;
   gint rows = 0;
   
   g_return_if_fail (!sibling || GTK_CTREE_ROW (sibling)->parent == parent);
@@ -2311,20 +2509,30 @@ gtk_ctree_link (GtkCTree *ctree,
 
   clist = GTK_CLIST (ctree);
 
+  if (update_focus_row && clist->selection_mode == GTK_SELECTION_BROWSE)
+    {
+      if (clist->anchor != -1)
+       GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
+      
+      g_list_free (clist->undo_selection);
+      g_list_free (clist->undo_unselection);
+      clist->undo_selection = NULL;
+      clist->undo_unselection = NULL;
+    }
+
   for (rows = 1, list_end = node; list_end->next; list_end = list_end->next)
     rows++;
 
   GTK_CTREE_ROW (node)->parent = parent;
   GTK_CTREE_ROW (node)->sibling = sibling;
 
-  if (parent)
+  if (!parent || (parent && (gtk_ctree_is_visible (ctree, parent) &&
+                            GTK_CTREE_ROW (parent)->expanded)))
     {
-      if ((visible = gtk_ctree_is_visible (ctree, parent)) &&
-         GTK_CTREE_ROW (parent)->expanded)
-       clist->rows += rows;
+      visible = TRUE;
+      clist->rows += rows;
     }
-  else
-    clist->rows += rows;
+      
 
   if (sibling)
     {
@@ -2407,13 +2615,24 @@ gtk_ctree_link (GtkCTree *ctree,
   if (clist->row_list_end == NULL || clist->row_list_end->next == node)
     clist->row_list_end = list_end;
 
-  if (!GTK_CLIST_FROZEN (clist) && visible)
-    gtk_clist_thaw (clist);
+  if (visible && update_focus_row)
+    {
+      gint pos;
+         
+      pos = g_list_position (clist->row_list, node);
+  
+      if (pos <= clist->focus_row)
+       {
+         clist->focus_row += rows;
+         clist->undo_anchor = clist->focus_row;
+       }
+    }
 }
 
 static void
 gtk_ctree_unlink (GtkCTree *ctree, 
-                 GList    *node)
+                 GList    *node,
+                  gboolean  update_focus_row)
 {
   GtkCList *clist;
   gint rows;
@@ -2428,6 +2647,17 @@ gtk_ctree_unlink (GtkCTree *ctree,
 
   clist = GTK_CLIST (ctree);
   
+  if (update_focus_row && clist->selection_mode == GTK_SELECTION_BROWSE)
+    {
+      if (clist->anchor != -1)
+       GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
+      
+      g_list_free (clist->undo_selection);
+      g_list_free (clist->undo_unselection);
+      clist->undo_selection = NULL;
+      clist->undo_unselection = NULL;
+    }
+
   visible = gtk_ctree_is_visible (ctree, node);
 
   /* clist->row_list_end unlinked ? */
@@ -2446,6 +2676,24 @@ gtk_ctree_unlink (GtkCTree *ctree,
       rows++;
     }
 
+  if (visible)
+    {
+      clist->rows -= (rows + 1);
+
+      if (update_focus_row)
+       {
+         gint pos;
+         
+         pos = g_list_position (clist->row_list, node);
+         if (pos + rows + 1 < clist->focus_row)
+           clist->focus_row -= (rows + 1);
+         else if (pos <= clist->focus_row)
+           clist->focus_row = pos - 1;
+         clist->undo_anchor = clist->focus_row;
+       }
+    }
+
+
   if (work)
     {
       work->prev->next = NULL;
@@ -2499,14 +2747,6 @@ gtk_ctree_unlink (GtkCTree *ctree,
          GTK_CTREE_ROW (sibling)->sibling = GTK_CTREE_ROW (node)->sibling;
        }
     }
-
-  if (visible)
-    {
-      clist->rows -= (rows + 1);
-
-      if (!GTK_CLIST_FROZEN (clist))
-       gtk_clist_thaw (clist);
-    }
 }
 
 static void
@@ -2534,6 +2774,17 @@ real_tree_move (GtkCTree *ctree,
 
   clist = GTK_CLIST (ctree);
 
+  if (clist->selection_mode == GTK_SELECTION_BROWSE)
+    {
+      if (clist->anchor != -1)
+       GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
+      
+      g_list_free (clist->undo_selection);
+      g_list_free (clist->undo_unselection);
+      clist->undo_selection = NULL;
+      clist->undo_unselection = NULL;
+    }
+
   if (ctree->auto_sort)
     {
       if (new_parent == GTK_CTREE_ROW (node)->parent)
@@ -2558,23 +2809,87 @@ real_tree_move (GtkCTree *ctree,
       thaw = TRUE;
     }
 
-  gtk_ctree_unlink (ctree, node);
-  gtk_ctree_link (ctree, node, new_parent, new_sibling);
+  work = NULL;
+  if (gtk_ctree_is_visible (ctree, node) ||
+      gtk_ctree_is_visible (ctree, new_sibling))
+    work = g_list_nth (clist->row_list, clist->focus_row);
+      
+  gtk_ctree_unlink (ctree, node, FALSE);
+  gtk_ctree_link (ctree, node, new_parent, new_sibling, FALSE);
+  
+  if (work)
+    {
+      while (work &&  !gtk_ctree_is_visible (ctree, work))
+       work = GTK_CTREE_ROW (work)->parent;
+      clist->focus_row = g_list_position (clist->row_list, work);
+      clist->undo_anchor = clist->focus_row;
+    }
 
   if (thaw)
     gtk_clist_thaw (clist);
 }
 
+static void
+change_focus_row_expansion (GtkCTree          *ctree,
+                           GtkCTreeExpansion  action)
+{
+  GtkCList *clist;
+  GList *node;
+
+  g_return_if_fail (ctree != NULL);
+  g_return_if_fail (GTK_IS_CTREE (ctree));
+
+  clist = GTK_CLIST (ctree);
+
+  if (gdk_pointer_is_grabbed ())
+    return;
+  
+  if (!(node = g_list_nth (clist->row_list, clist->focus_row)) ||
+      GTK_CTREE_ROW (node)->is_leaf || !(GTK_CTREE_ROW (node)->children))
+    return;
+
+  switch (action)
+    {
+    case GTK_CTREE_EXPANSION_EXPAND:
+      gtk_ctree_expand (ctree, node);
+      break;
+    case GTK_CTREE_EXPANSION_EXPAND_RECURSIVE:
+      gtk_ctree_expand_recursive (ctree, node);
+      break;
+    case GTK_CTREE_EXPANSION_COLLAPSE:
+      gtk_ctree_collapse (ctree, node);
+      break;
+    case GTK_CTREE_EXPANSION_COLLAPSE_RECURSIVE:
+      gtk_ctree_collapse_recursive (ctree, node);
+      break;
+    case GTK_CTREE_EXPANSION_TOGGLE:
+      gtk_ctree_toggle_expansion (ctree, node);
+      break;
+    case GTK_CTREE_EXPANSION_TOGGLE_RECURSIVE:
+      gtk_ctree_toggle_expansion_recursive (ctree, node);
+      break;
+    }
+}
+
 static void 
 real_tree_expand (GtkCTree *ctree,
                  GList    *node)
 {
+  GtkCList *clist;
   GList *work;
   gint level;
 
+  g_return_if_fail (ctree != NULL);
+  g_return_if_fail (GTK_IS_CTREE (ctree));
+
   if (!node || GTK_CTREE_ROW (node)->expanded)
     return;
 
+  clist = GTK_CLIST (ctree);
+  
+  if (clist->selection_mode == GTK_SELECTION_EXTENDED && clist->anchor >= 0)
+    GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
+
   GTK_CTREE_ROW (node)->expanded = TRUE;
   level = GTK_CTREE_ROW (node)->level;
 
@@ -2591,7 +2906,11 @@ real_tree_expand (GtkCTree *ctree,
   work = GTK_CTREE_ROW (node)->children;
   if (work)
     {
+      GtkCList *clist;
       gint tmp = 0;
+      gint row;
+
+      clist = GTK_CLIST (ctree);
 
       while (work->next)
        {
@@ -2604,15 +2923,18 @@ real_tree_expand (GtkCTree *ctree,
       if (node->next)
        node->next->prev = work;
       else
-       GTK_CLIST (ctree)->row_list_end = work;
+       clist->row_list_end = work;
 
       node->next = GTK_CTREE_ROW (node)->children;
       
       if (gtk_ctree_is_visible (ctree, node))
        {
-         GTK_CLIST (ctree)->rows += tmp + 1;
+         row = g_list_position (clist->row_list, node);
+         if (row < clist->focus_row)
+           clist->focus_row += tmp + 1;
+         clist->rows += tmp + 1;
          if (!GTK_CLIST_FROZEN (ctree))
-           gtk_clist_thaw (GTK_CLIST (ctree));
+           gtk_clist_thaw (clist);
        }
     }
 }
@@ -2621,12 +2943,21 @@ static void
 real_tree_collapse (GtkCTree *ctree,
                    GList    *node)
 {
+  GtkCList *clist;
   GList *work;
   gint level;
 
+  g_return_if_fail (ctree != NULL);
+  g_return_if_fail (GTK_IS_CTREE (ctree));
+
   if (!node || !GTK_CTREE_ROW (node)->expanded)
     return;
 
+  clist = GTK_CLIST (ctree);
+
+  if (clist->selection_mode == GTK_SELECTION_EXTENDED && clist->anchor >= 0)
+    GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
+  
   GTK_CTREE_ROW (node)->expanded = FALSE;
   level = GTK_CTREE_ROW (node)->level;
 
@@ -2643,7 +2974,11 @@ real_tree_collapse (GtkCTree *ctree,
   work = GTK_CTREE_ROW (node)->children;
   if (work)
     {
+      GtkCList *clist;
       gint tmp = 0;
+      gint row;
+
+      clist = GTK_CLIST (ctree);
 
       while (work && GTK_CTREE_ROW (work)->level > level)
        {
@@ -2660,14 +2995,17 @@ real_tree_collapse (GtkCTree *ctree,
       else
        {
          node->next = NULL;
-         GTK_CLIST (ctree)->row_list_end = node;
+         clist->row_list_end = node;
        }
 
       if (gtk_ctree_is_visible (ctree, node))
        {
-         GTK_CLIST (ctree)->rows -= tmp;
+         row = g_list_position (clist->row_list, node);
+         if (row < clist->focus_row)
+           clist->focus_row -= tmp;
+         clist->rows -= tmp;
          if (!GTK_CLIST_FROZEN (ctree))
-           gtk_clist_thaw (GTK_CLIST (ctree));
+           gtk_clist_thaw (clist);
        }
     }
 }
@@ -2812,8 +3150,8 @@ tree_delete (GtkCTree *ctree,
       work = g_list_find (clist->selection, node);
       if (work)
        {
-         if (ctree->selection_last && ctree->selection_last == work)
-           ctree->selection_last = ctree->selection_last->prev;
+         if (clist->selection_end && clist->selection_end == work)
+           clist->selection_end = clist->selection_end->prev;
          clist->selection = g_list_remove (clist->selection, node);
        }
     }
@@ -2886,6 +3224,22 @@ tree_collapse (GtkCTree *ctree,
                     data);
 }
 
+static void
+tree_toggle_expansion (GtkCTree *ctree,
+                      GList    *node,
+                      gpointer  data)
+{
+  if (!node)
+    return;
+
+  if (GTK_CTREE_ROW (node)->expanded)
+    gtk_signal_emit (GTK_OBJECT (ctree), ctree_signals[TREE_COLLAPSE], node,
+                    data);
+  else
+    gtk_signal_emit (GTK_OBJECT (ctree), ctree_signals[TREE_EXPAND], node,
+                    data);
+}
+
 static GtkCTreeRow *
 row_new (GtkCTree *ctree)
 {
@@ -3002,13 +3356,47 @@ cell_empty (GtkCList    *clist,
   clist_row->cell[column].type = GTK_CELL_EMPTY;
 }
 
+static void
+real_select_row (GtkCList *clist,
+                gint      row,
+                gint      column,
+                GdkEvent *event)
+{
+  GList *node;
+
+  g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CTREE (clist));
+  
+  if ((node = g_list_nth (clist->row_list, row)))
+    gtk_signal_emit (GTK_OBJECT (clist), ctree_signals[TREE_SELECT_ROW],
+                    node, event);
+}
+
+static void
+real_unselect_row (GtkCList *clist,
+                  gint      row,
+                  gint      column,
+                  GdkEvent *event)
+{
+  GList *node;
+
+  g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CTREE (clist));
+
+  if ((node = g_list_nth (clist->row_list, row)))
+    gtk_signal_emit (GTK_OBJECT (clist), ctree_signals[TREE_UNSELECT_ROW],
+                    node, event);
+}
+
 static void
 real_tree_select (GtkCTree *ctree,
                  GList    *node,
                  gint      column)
 {
   GtkCList *clist;
-  GList *selection;
+  GList *list;
+  GList *sel_row;
+  gboolean node_selected;
 
   g_return_if_fail (ctree != NULL);
 
@@ -3017,15 +3405,12 @@ real_tree_select (GtkCTree *ctree,
 
   clist = GTK_CLIST (ctree);
 
-  if (GTK_CTREE_ROW (node)->row.state == GTK_STATE_SELECTED)
-    return;
-
-  if (clist->selection_mode == GTK_SELECTION_SINGLE ||
-      clist->selection_mode == GTK_SELECTION_BROWSE)
+  switch (clist->selection_mode)
     {
-      GList *list;
-      GList *sel_row;
+    case GTK_SELECTION_SINGLE:
+    case GTK_SELECTION_BROWSE:
 
+      node_selected = FALSE;
       list = clist->selection;
 
       while (list)
@@ -3033,27 +3418,29 @@ real_tree_select (GtkCTree *ctree,
          sel_row = list->data;
          list = list->next;
          
-         if (node != sel_row && 
-             GTK_CTREE_ROW (sel_row)->row.state == GTK_STATE_SELECTED)
-           gtk_signal_emit (GTK_OBJECT (ctree), 
-                            ctree_signals[TREE_UNSELECT_ROW], sel_row, 
-                            column);
+         if (node == sel_row)
+           node_selected = TRUE;
+         else
+           gtk_signal_emit (GTK_OBJECT (ctree),
+                            ctree_signals[TREE_UNSELECT_ROW], sel_row, column);
        }
+
+      if (node_selected)
+       return;
+
+    default:
+      break;
     }
 
   GTK_CTREE_ROW (node)->row.state = GTK_STATE_SELECTED;
-  selection = g_list_alloc ();
-  selection->data = node;
 
-  if (ctree->selection_last)
+  if (!clist->selection)
     {
-      ctree->selection_last->next = selection;
-      selection->prev = ctree->selection_last;
+      clist->selection = g_list_append (clist->selection, node);
+      clist->selection_end = clist->selection;
     }
-  else 
-    clist->selection = selection;
-
-  ctree->selection_last = selection;
+  else
+    clist->selection_end = g_list_append (clist->selection_end, node)->next;
 
   tree_draw_row (ctree, node);
 }
@@ -3070,15 +3457,16 @@ real_tree_unselect (GtkCTree *ctree,
   if (!node)
     return;
 
+  clist = GTK_CLIST (ctree);
+
   if (GTK_CTREE_ROW (node)->row.state != GTK_STATE_SELECTED)
     return;
 
-  clist = GTK_CLIST (ctree);
-
-  if (ctree->selection_last && ctree->selection_last->data == node)
-    ctree->selection_last = ctree->selection_last->prev;
+  if (clist->selection_end && clist->selection_end->data == node)
+    clist->selection_end = clist->selection_end->prev;
 
   clist->selection = g_list_remove (clist->selection, node);
+  
   GTK_CTREE_ROW (node)->row.state = GTK_STATE_NORMAL;
 
   tree_draw_row (ctree, node);
@@ -3089,7 +3477,14 @@ tree_toggle_selection (GtkCTree *ctree,
                       GList    *node,
                       gint      column)
 {
-  switch (GTK_CLIST (ctree)->selection_mode)
+  GtkCList *clist;
+  
+  g_return_if_fail (ctree != NULL);
+  g_return_if_fail (GTK_IS_CTREE (ctree));
+
+  clist = GTK_CLIST (ctree);
+
+  switch (clist->selection_mode)
     {
     case GTK_SELECTION_SINGLE:
     case GTK_SELECTION_MULTIPLE:
@@ -3109,28 +3504,135 @@ tree_toggle_selection (GtkCTree *ctree,
 
     case GTK_SELECTION_EXTENDED:
       break;
-
-    default:
-      break;
     }
 }
 
-static gboolean
-ctree_is_hot_spot (GtkCTree *ctree, 
-                  GList    *node,
-                  gint      row, 
-                  gint      x, 
-                  gint      y)
+static void
+select_row_recursive (GtkCTree *ctree, 
+                     GList    *node, 
+                     gpointer  data)
 {
-  GtkCTreeRow *tree_row;
-  GtkCList *clist;
-  GtkCellPixText *cell;
-  gint xl;
-  gint yu;
-  
-  g_return_val_if_fail (ctree != NULL, FALSE);
-  g_return_val_if_fail (GTK_IS_CTREE (ctree), FALSE);
-  g_return_val_if_fail (node != NULL, FALSE);
+  if (!node || GTK_CTREE_ROW (node)->row.state == GTK_STATE_SELECTED)
+    return;
+
+  GTK_CLIST (ctree)->undo_unselection = 
+    g_list_prepend (GTK_CLIST (ctree)->undo_unselection, node);
+  gtk_ctree_select (ctree, node);
+}
+
+static void
+real_select_all (GtkCList *clist)
+{
+  GtkCTree *ctree;
+  GList *node;
+  gboolean thaw = FALSE;
+  
+  g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CTREE (clist));
+
+  ctree = GTK_CTREE (clist);
+
+  switch (clist->selection_mode)
+    {
+    case GTK_SELECTION_SINGLE:
+    case GTK_SELECTION_BROWSE:
+      return;
+
+    case GTK_SELECTION_EXTENDED:
+
+      if (!GTK_CLIST_FROZEN (clist))
+       {
+         gtk_clist_freeze (clist);
+         thaw = TRUE;
+       }
+
+      g_list_free (clist->undo_selection);
+      g_list_free (clist->undo_unselection);
+      clist->undo_selection = NULL;
+      clist->undo_unselection = NULL;
+         
+      clist->anchor_state = GTK_STATE_SELECTED;
+      clist->anchor = -1;
+      clist->drag_pos = -1;
+      clist->undo_anchor = clist->focus_row;
+
+      for (node = clist->row_list; node; node = node->next)
+       gtk_ctree_pre_recursive (ctree, node, select_row_recursive, NULL);
+
+      if (thaw)
+       gtk_clist_thaw (clist);
+      break;
+
+    case GTK_SELECTION_MULTIPLE:
+      gtk_ctree_select_recursive (ctree, NULL);
+      break;;
+    }
+}
+
+static void
+real_unselect_all (GtkCList *clist)
+{
+  GtkCTree *ctree;
+  GList *list;
+  GList *node;
+  g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CTREE (clist));
+  
+  ctree = GTK_CTREE (clist);
+
+  switch (clist->selection_mode)
+    {
+    case GTK_SELECTION_BROWSE:
+      if (clist->focus_row >= 0)
+       {
+         gtk_ctree_select
+           (ctree, g_list_nth (clist->row_list, clist->focus_row));
+         return;
+       }
+      break;
+
+    case GTK_SELECTION_EXTENDED:
+      g_list_free (clist->undo_selection);
+      g_list_free (clist->undo_unselection);
+      clist->undo_selection = NULL;
+      clist->undo_unselection = NULL;
+
+      clist->anchor = -1;
+      clist->drag_pos = -1;
+      clist->undo_anchor = clist->focus_row;
+      break;
+
+    default:
+      break;
+    }
+
+  list = clist->selection;
+
+  while (list)
+    {
+      node = list->data;
+      list = list->next;
+      gtk_ctree_unselect (ctree, node);
+    }
+}
+
+static gboolean
+ctree_is_hot_spot (GtkCTree *ctree, 
+                  GList    *node,
+                  gint      row, 
+                  gint      x, 
+                  gint      y)
+{
+  GtkCTreeRow *tree_row;
+  GtkCList *clist;
+  GtkCellPixText *cell;
+  gint xl;
+  gint yu;
+  
+  g_return_val_if_fail (ctree != NULL, FALSE);
+  g_return_val_if_fail (GTK_IS_CTREE (ctree), FALSE);
+  g_return_val_if_fail (node != NULL, FALSE);
 
   tree_row = GTK_CTREE_ROW (node);
   clist = GTK_CLIST (ctree);
@@ -3290,7 +3792,10 @@ gtk_ctree_insert (GtkCTree  *ctree,
        sibling = GTK_CTREE_ROW (sibling)->sibling;
     }
 
-  gtk_ctree_link (ctree, node, parent, sibling);
+  gtk_ctree_link (ctree, node, parent, sibling, TRUE);
+
+  if (!GTK_CLIST_FROZEN (clist))
+    gtk_clist_thaw (clist);
 
   return node;
 }
@@ -3315,55 +3820,40 @@ gtk_ctree_remove (GtkCTree *ctree,
 
   if (node)
     {
-      gtk_ctree_unlink (ctree, node);
-      gtk_ctree_post_recursive (ctree, node, GTK_CTREE_FUNC (tree_delete), 
+      gtk_ctree_unlink (ctree, node, TRUE);
+      gtk_ctree_post_recursive (ctree, node, GTK_CTREE_FUNC (tree_delete),
                                NULL);
     }
   else
-    {
-      GList *work;
-      GList *ptr;
-
-      work = clist->row_list;
-      clist->row_list = NULL;
-      clist->row_list_end = NULL;
-      clist->rows = 0;
-
-      while (work)
-       {
-         ptr = work;
-         work = GTK_CTREE_ROW (work)->sibling;
-         gtk_ctree_post_recursive (ctree, ptr, GTK_CTREE_FUNC (tree_delete), 
-                                   NULL);
-       }
-    }
+    gtk_clist_clear (clist);
 
   if (thaw)
     gtk_clist_thaw (clist);
 }
 
-void
-gtk_ctree_clear (GtkCTree *ctree)
+static void
+real_clear (GtkCList *clist)
 {
-  GtkCList *clist;
+  GtkCTree *ctree;
   GList *work;
   GList *ptr;
 
-  g_return_if_fail (ctree != NULL);
-  g_return_if_fail (GTK_IS_CTREE (ctree));
+  g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CTREE (clist));
 
-  clist = GTK_CLIST (ctree);
+  ctree = GTK_CTREE (clist);
 
-  /* free up the selection list */
-  g_list_free (clist->selection);
-  clist->selection = NULL;
-  ctree->selection_last = NULL;
+  ctree->drag_row       = -1;
+  ctree->drag_rect      = FALSE;
+  ctree->in_drag        = FALSE;
+  ctree->drag_source    = NULL;
+  ctree->drag_target    = NULL;
+  ctree->drag_icon      = NULL;
 
   /* remove all the rows */
   work = clist->row_list;
   clist->row_list = NULL;
   clist->row_list_end = NULL;
-  clist->rows = 0;
 
   while (work)
     {
@@ -3373,25 +3863,7 @@ gtk_ctree_clear (GtkCTree *ctree)
                                NULL);
     }
 
-  clist->voffset = 0;
-
-  ctree->drag_row       = -1;
-  ctree->drag_rect      = FALSE;
-  ctree->in_drag        = FALSE;
-  ctree->drag_source    = NULL;
-  ctree->drag_target    = NULL;
-  ctree->drag_icon      = NULL;
-
-  /* zero-out the scrollbars */
-  if (clist->vscrollbar)
-    {
-      GTK_RANGE (clist->vscrollbar)->adjustment->value = 0.0;
-      gtk_signal_emit_by_name 
-       (GTK_OBJECT (GTK_RANGE (clist->vscrollbar)->adjustment), "changed");
-      
-      if (!GTK_CLIST_FROZEN (clist))
-       gtk_clist_thaw (clist);
-    }
+  (parent_class->clear) (clist);
 }
 
 
@@ -3422,7 +3894,8 @@ gtk_ctree_post_recursive (GtkCTree     *ctree,
       work = tmp;
     }
 
-  (* func) (ctree, node, data);
+  if (node)
+    func (ctree, node, data);
 }
 
 void
@@ -3434,10 +3907,12 @@ gtk_ctree_pre_recursive (GtkCTree     *ctree,
   GList *work;
   GList *tmp;
 
-  (* func) (ctree, node, data);
 
   if (node)
-    work = GTK_CTREE_ROW (node)->children;
+    {
+      work = GTK_CTREE_ROW (node)->children;
+      func (ctree, node, data);
+    }
   else
     work = GTK_CLIST (ctree)->row_list;
 
@@ -3595,6 +4070,9 @@ gtk_ctree_expand (GtkCTree *ctree,
   g_return_if_fail (ctree != NULL);
   g_return_if_fail (node != NULL);
   
+  if (GTK_CTREE_ROW (node)->is_leaf)
+    return;
+
   gtk_signal_emit (GTK_OBJECT (ctree), ctree_signals[TREE_EXPAND], node);
 }
 
@@ -3609,7 +4087,7 @@ gtk_ctree_expand_recursive (GtkCTree *ctree,
 
   clist = GTK_CLIST (ctree);
 
-  if (node && !GTK_CTREE_ROW (node)->children)
+  if (node && GTK_CTREE_ROW (node)->is_leaf)
     return;
 
   if (((node && gtk_ctree_is_visible (ctree, node)) || !node) && 
@@ -3632,6 +4110,9 @@ gtk_ctree_collapse (GtkCTree *ctree,
   g_return_if_fail (ctree != NULL);
   g_return_if_fail (node != NULL);
   
+  if (GTK_CTREE_ROW (node)->is_leaf)
+    return;
+
   gtk_signal_emit (GTK_OBJECT (ctree), ctree_signals[TREE_COLLAPSE], node);
 }
 
@@ -3644,11 +4125,11 @@ gtk_ctree_collapse_recursive (GtkCTree *ctree,
 
   g_return_if_fail (ctree != NULL);
 
-  clist = GTK_CLIST (ctree);
-
-  if (node && !GTK_CTREE_ROW (node)->children)
+  if (node && GTK_CTREE_ROW (node)->is_leaf)
     return;
 
+  clist = GTK_CLIST (ctree);
+
   if (((node && gtk_ctree_is_visible (ctree, node)) || !node) && 
       !GTK_CLIST_FROZEN (clist))
     {
@@ -3662,6 +4143,47 @@ gtk_ctree_collapse_recursive (GtkCTree *ctree,
     gtk_clist_thaw (clist);
 }
 
+void
+gtk_ctree_toggle_expansion (GtkCTree *ctree,
+                           GList    *node)
+{
+  g_return_if_fail (ctree != NULL);
+  g_return_if_fail (node != NULL);
+  
+  if (GTK_CTREE_ROW (node)->is_leaf)
+    return;
+
+  tree_toggle_expansion (ctree, node, NULL);
+}
+
+void 
+gtk_ctree_toggle_expansion_recursive (GtkCTree *ctree,
+                                     GList    *node)
+{
+  GtkCList *clist;
+  gboolean thaw = FALSE;
+
+  g_return_if_fail (ctree != NULL);
+  
+  if (node && GTK_CTREE_ROW (node)->is_leaf)
+    return;
+
+  clist = GTK_CLIST (ctree);
+
+  if (((node && gtk_ctree_is_visible (ctree, node)) || !node) && 
+      !GTK_CLIST_FROZEN (clist))
+    {
+      gtk_clist_freeze (clist);
+      thaw = TRUE;
+    }
+  
+  gtk_ctree_post_recursive (ctree, node,
+                           GTK_CTREE_FUNC (tree_toggle_expansion), NULL);
+
+  if (thaw)
+    gtk_clist_thaw (clist);
+}
+
 void
 gtk_ctree_select (GtkCTree *ctree, 
                  GList    *node)
@@ -3708,8 +4230,9 @@ gtk_ctree_real_select_recursive (GtkCTree *ctree,
 
   clist = GTK_CLIST (ctree);
 
-  if (clist->selection_mode == GTK_SELECTION_EXTENDED ||
-      (state && clist->selection_mode !=  GTK_SELECTION_MULTIPLE) ||
+  if ((state && 
+       (clist->selection_mode !=  GTK_SELECTION_MULTIPLE ||
+       clist->selection_mode == GTK_SELECTION_EXTENDED)) ||
       (!state && clist->selection_mode ==  GTK_SELECTION_BROWSE))
     return;
 
@@ -4052,53 +4575,6 @@ gtk_ctree_set_background (GtkCTree   *ctree,
   tree_draw_row (ctree, node);
 }
 
-void
-gtk_ctree_set_selection_mode (GtkCTree         *ctree,
-                              GtkSelectionMode  mode)
-{
-  GtkCList *clist;
-  GList *selection;
-  GList *work;
-  gboolean thaw = FALSE;
-  
-  g_return_if_fail (ctree != NULL);
-  g_return_if_fail (GTK_IS_CTREE (ctree));
-
-  clist = GTK_CLIST (ctree);
-
-  if (mode == clist->selection_mode)
-    return;
-
-  clist->selection_mode = mode;
-
-  if (mode == GTK_SELECTION_MULTIPLE)
-    return;
-
-  selection = clist->selection;
-  if (selection && mode == GTK_SELECTION_BROWSE)
-    selection = selection->next;
-
-  if (!selection)
-    return;
-       
-  if (!GTK_CLIST_FROZEN (clist))
-    {
-      gtk_clist_freeze (clist);
-      thaw = TRUE;
-    }
-
-  while (selection)
-    {
-      work = selection->data;
-      selection = selection->next;
-      gtk_signal_emit (GTK_OBJECT (ctree), 
-                      ctree_signals[TREE_UNSELECT_ROW], work);
-    }
-
-  if (thaw)
-    gtk_clist_thaw (clist);
-}
-
 void
 gtk_ctree_set_row_data (GtkCTree *ctree,
                         GList    *node,
@@ -4131,62 +4607,27 @@ gtk_ctree_get_row_data (GtkCTree *ctree,
 }
 
 void
-gtk_ctree_scroll_to (GtkCTree *ctree,
-                    GList    *node,
-                    gint      column,
-                    gfloat    row_align,
-                    gfloat    col_align)
+gtk_ctree_moveto (GtkCTree *ctree,
+                 GList    *node,
+                 gint      column,
+                 gfloat    row_align,
+                 gfloat    col_align)
 {
-  gint x;
-  gint y;
-  gint row = 0;
+  gint row = -1;
   GtkCList *clist;
-  GList *work;
-  GtkAdjustment *adj;
 
   g_return_if_fail (ctree != NULL);
   g_return_if_fail (GTK_IS_CTREE (ctree));
 
   clist = GTK_CLIST (ctree);
 
-  if (node && !gtk_ctree_is_visible (ctree, node))
-    return;
+  while (node && !gtk_ctree_is_visible (ctree, node))
+    node = GTK_CTREE_ROW (node)->parent;
 
-  /* adjust vertical scrollbar */
   if (node)
-    {
-      adj = GTK_RANGE (clist->vscrollbar)->adjustment;
-      work = clist->row_list;
-      while (work && work != node)
-       {
-         work = work->next;
-         row++;
-       }
-
-      if (!work)
-       return;
-
-      y = ROW_TOP_YPIXEL (clist, row) - clist->voffset;
-
-      y = y - row_align * (clist->clist_window_height - clist->row_height)
-       + (2 * row_align - 1) * CELL_SPACING;
-
-      if (y + adj->page_size > adj->upper)
-       gtk_adjustment_set_value (adj, adj->upper - adj->page_size);
-      else
-       gtk_adjustment_set_value (adj, y);
-    }
-     
-  /* adjust horizontal scrollbar */
-  if (column >= 0)
-    {
-      adj = GTK_RANGE (clist->hscrollbar)->adjustment;
-      x = COLUMN_LEFT (clist, column);
-      x = x - col_align * 
-       (clist->clist_window_width - clist->column[column].area.width)
-       + (2 * col_align - 1) * COLUMN_INSET;
-      gtk_adjustment_set_value (adj, x);
-    }
+    row = g_list_position (clist->row_list, node);
+  
+  gtk_clist_moveto (clist, row, column, row_align, col_align);
 }
 
 
@@ -4350,8 +4791,8 @@ tree_sort (GtkCTree *ctree,
        list_start = GTK_CTREE_ROW (max)->sibling;
       else
        {
-         gtk_ctree_unlink (ctree,max);
-         gtk_ctree_link (ctree, max, node, list_start);
+         gtk_ctree_unlink (ctree, max, FALSE);
+         gtk_ctree_link (ctree, max, node, list_start, FALSE);
        }
     }
 }
@@ -4361,6 +4802,7 @@ gtk_ctree_sort_recursive (GtkCTree *ctree,
                          GList    *node)
 {
   GtkCList *clist;
+  GList *focus_node = NULL;
   gboolean thaw = FALSE;
 
   g_return_if_fail (ctree != NULL);
@@ -4374,8 +4816,28 @@ gtk_ctree_sort_recursive (GtkCTree *ctree,
       thaw = TRUE;
     }
 
+  if (clist->selection_mode == GTK_SELECTION_BROWSE)
+    {
+      if (clist->anchor != -1)
+       GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
+      
+      g_list_free (clist->undo_selection);
+      g_list_free (clist->undo_unselection);
+      clist->undo_selection = NULL;
+      clist->undo_unselection = NULL;
+    }
+
+  if (gtk_ctree_is_visible (ctree, node))
+    focus_node = g_list_nth (clist->row_list, clist->focus_row);
+      
   gtk_ctree_post_recursive (ctree, node, GTK_CTREE_FUNC (tree_sort), NULL);
 
+  if (focus_node)
+    {
+      clist->focus_row = g_list_position (clist->row_list, focus_node);
+      clist->undo_anchor = clist->focus_row;
+    }
+
   if (thaw)
     gtk_clist_thaw (clist);
 }
@@ -4385,6 +4847,7 @@ gtk_ctree_sort (GtkCTree *ctree,
                GList    *node)
 {
   GtkCList *clist;
+  GList *focus_node = NULL;
   gboolean thaw = FALSE;
 
   g_return_if_fail (ctree != NULL);
@@ -4398,8 +4861,209 @@ gtk_ctree_sort (GtkCTree *ctree,
       thaw = TRUE;
     }
 
+  if (clist->selection_mode == GTK_SELECTION_BROWSE)
+    {
+      if (clist->anchor != -1)
+       GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
+      
+      g_list_free (clist->undo_selection);
+      g_list_free (clist->undo_unselection);
+      clist->undo_selection = NULL;
+      clist->undo_unselection = NULL;
+    }
+
+  if (gtk_ctree_is_visible (ctree, node))
+    focus_node = g_list_nth (clist->row_list, clist->focus_row);
+
   tree_sort (ctree, node, NULL);
 
+  if (focus_node)
+    {
+      clist->focus_row = g_list_position (clist->row_list, focus_node);
+      clist->undo_anchor = clist->focus_row;
+    }
+
   if (thaw)
     gtk_clist_thaw (clist);
 }
+
+/************************************************************************/
+
+static void
+fake_unselect_all (GtkCList *clist,
+                  gint      row)
+{
+  GList *list;
+  GList *focus_node = NULL;
+
+  if (row >= 0 && (focus_node = g_list_nth (clist->row_list, row)))
+    {
+      if (GTK_CTREE_ROW (focus_node)->row.state == GTK_STATE_NORMAL)
+       {
+         GTK_CTREE_ROW (focus_node)->row.state = GTK_STATE_SELECTED;
+         
+         if (!GTK_CLIST_FROZEN (clist) &&
+             gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
+           GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row,
+                                                 GTK_CLIST_ROW (focus_node));
+       }  
+    }
+
+  clist->undo_selection = clist->selection;
+  clist->selection = NULL;
+  clist->selection_end = NULL;
+  
+  for (list = clist->undo_selection; list; list = list->next)
+    {
+      if (list->data == focus_node)
+       continue;
+
+      GTK_CTREE_ROW ((GList *)(list->data))->row.state = GTK_STATE_NORMAL;
+      tree_draw_row (GTK_CTREE (clist), (GList *)(list->data));
+    }
+}
+
+static GList *
+selection_find (GtkCList *clist,
+               gint      row_number,
+               GList    *row_list_element)
+{
+  return g_list_find (clist->selection, row_list_element);
+}
+
+static void
+resync_selection (GtkCList *clist, GdkEvent *event)
+{
+  GtkCTree *ctree;
+  GList *list;
+  GList *node;
+  gint i;
+  gint e;
+  gint row;
+  gboolean thaw = FALSE;
+  gboolean unselect;
+
+  if (clist->anchor < 0)
+    return;
+
+  ctree = GTK_CTREE (clist);
+  
+  if (!GTK_CLIST_FROZEN (clist))
+    {
+      GTK_CLIST_SET_FLAG (clist, CLIST_FROZEN);
+      thaw = TRUE;
+    }
+
+  i = MIN (clist->anchor, clist->drag_pos);
+  e = MAX (clist->anchor, clist->drag_pos);
+
+  if (clist->undo_selection)
+    {
+      list = clist->selection;
+      clist->selection = clist->undo_selection;
+      clist->selection_end = g_list_last (clist->selection);
+      clist->undo_selection = list;
+      list = clist->selection;
+
+      while (list)
+       {
+         node = list->data;
+         list = list->next;
+         
+         unselect = TRUE;
+
+         if (gtk_ctree_is_visible (ctree, node))
+           {
+             row = g_list_position (clist->row_list, node);
+             if (row >= i && row <= e)
+               unselect = FALSE;
+           }
+         if (unselect)
+           {
+             GTK_CTREE_ROW (node)->row.state = GTK_STATE_SELECTED;
+             gtk_ctree_unselect (ctree, node);
+             clist->undo_selection = g_list_prepend (clist->undo_selection,
+                                                     node);
+           }
+       }
+    }    
+
+
+  for (node = g_list_nth (clist->row_list, i); i <= e; i++, node = node->next)
+    if (g_list_find (clist->selection, node))
+      {
+       if (GTK_CTREE_ROW (node)->row.state == GTK_STATE_NORMAL)
+         {
+           GTK_CTREE_ROW (node)->row.state = GTK_STATE_SELECTED;
+           gtk_ctree_unselect (ctree, node);
+           clist->undo_selection = g_list_prepend (clist->undo_selection,
+                                                   node);
+         }
+      }
+    else if (GTK_CTREE_ROW (node)->row.state == GTK_STATE_SELECTED)
+      {
+       GTK_CTREE_ROW (node)->row.state = GTK_STATE_NORMAL;
+       clist->undo_unselection = g_list_prepend (clist->undo_unselection,
+                                                 node);
+      }
+
+  for (list = clist->undo_unselection; list; list = list->next)
+    gtk_ctree_select (ctree, list->data);
+
+  clist->anchor = -1;
+  clist->drag_pos = -1;
+
+  if (thaw)
+    GTK_CLIST_UNSET_FLAG (clist, CLIST_FROZEN);
+}
+
+static void
+real_undo_selection (GtkCList *clist)
+{
+  GtkCTree *ctree;
+  GList *work;
+
+
+  g_return_if_fail (clist != NULL);
+  g_return_if_fail (GTK_IS_CLIST (clist));
+
+  if (clist->selection_mode != GTK_SELECTION_EXTENDED)
+    return;
+
+  if (!(clist->undo_selection || clist->undo_unselection))
+    {
+      gtk_clist_unselect_all (clist);
+      return;
+    }
+
+  ctree = GTK_CTREE (clist);
+
+  for (work = clist->undo_selection; work; work = work->next)
+    gtk_ctree_select (ctree, (GList *) work->data);
+
+  for (work = clist->undo_unselection; work; work = work->next)
+    gtk_ctree_unselect (ctree, (GList *) work->data);
+
+  if (GTK_WIDGET_HAS_FOCUS (clist) && clist->focus_row != clist->undo_anchor)
+    {
+      gtk_widget_draw_focus (GTK_WIDGET (clist));
+      clist->focus_row = clist->undo_anchor;
+      gtk_widget_draw_focus (GTK_WIDGET (clist));
+    }
+  else
+    clist->focus_row = clist->undo_anchor;
+  
+  clist->undo_anchor = -1;
+  g_list_free (clist->undo_selection);
+  g_list_free (clist->undo_unselection);
+  clist->undo_selection = NULL;
+  clist->undo_unselection = NULL;
+
+  if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
+      clist->clist_window_height)
+    gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
+  else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
+    gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
+
+}
index 7f2fdc33c3cb54e4432586fda5d25995a446e290..d13f6ca1c47ed0fcfa1b16fbea659a5b725c21e0 100644 (file)
@@ -37,7 +37,7 @@ extern "C"
   (GTK_CHECK_CLASS_CAST ((klass), gtk_ctree_get_type (), GtkCTreeClass))
 #define GTK_IS_CTREE(obj) \
   (GTK_CHECK_TYPE ((obj), gtk_ctree_get_type ()))
-#define GTK_CTREE_ROW(glist) ((GtkCTreeRow *)((glist)->data))
+#define GTK_CTREE_ROW(_glist_) ((GtkCTreeRow *)((_glist_)->data))
 #define GTK_CTREE_TREE(_ctree_, _glist_) \
   ((GtkCellTree *) &(((GtkCTreeRow *)((_glist_)->data))->cell[(_ctree_)->tree_col]))
 
@@ -58,6 +58,16 @@ typedef enum
   GTK_CTREE_LINES_NONE
 } GtkCTreeLineStyle;
 
+typedef enum
+{
+  GTK_CTREE_EXPANSION_EXPAND,
+  GTK_CTREE_EXPANSION_EXPAND_RECURSIVE,
+  GTK_CTREE_EXPANSION_COLLAPSE,
+  GTK_CTREE_EXPANSION_COLLAPSE_RECURSIVE,
+  GTK_CTREE_EXPANSION_TOGGLE,
+  GTK_CTREE_EXPANSION_TOGGLE_RECURSIVE
+} GtkCTreeExpansion;
+
 typedef struct _GtkCTree      GtkCTree;
 typedef struct _GtkCTreeClass GtkCTreeClass;
 typedef struct _GtkCTreeRow   GtkCTreeRow;
@@ -82,7 +92,6 @@ struct _GtkCTree
 
   gint tree_indent;
   gint tree_column;
-  GList *selection_last;
   gint drag_row;
   GList *drag_source;
   GList *drag_target;
@@ -115,6 +124,8 @@ struct _GtkCTreeClass
                             GList    *node,
                             GList    *new_parent,
                             GList    *new_sibling);
+  void (*change_focus_row_expansion) (GtkCTree *ctree,
+                                     GtkCTreeExpansion action);
 };
 
 struct _GtkCTreeRow
@@ -164,7 +175,6 @@ GList *     gtk_ctree_insert                (GtkCTree     *ctree,
                                             gboolean      expanded);
 void       gtk_ctree_remove                 (GtkCTree     *ctree, 
                                             GList        *node);
-void       gtk_ctree_clear                  (GtkCTree     *ctree);
 
 /***********************************************************
  *  Generic recursive functions, querying / finding tree   *
@@ -214,6 +224,10 @@ void       gtk_ctree_collapse               (GtkCTree     *ctree,
                                             GList        *node);
 void       gtk_ctree_collapse_recursive     (GtkCTree     *ctree,
                                             GList        *node);
+void       gtk_ctree_toggle_expansion       (GtkCTree     *ctree,
+                                            GList        *node);
+void       gtk_ctree_toggle_expansion_recursive (GtkCTree *ctree,
+                                                GList    *node);
 void       gtk_ctree_select                 (GtkCTree     *ctree, 
                                             GList        *node);
 void       gtk_ctree_select_recursive       (GtkCTree     *ctree, 
@@ -296,8 +310,6 @@ void       gtk_ctree_set_foreground         (GtkCTree     *ctree,
 void       gtk_ctree_set_background         (GtkCTree     *ctree,
                                             GList        *node,
                                             GdkColor     *color);
-void       gtk_ctree_set_selection_mode     (GtkCTree     *ctree,
-                                            GtkSelectionMode  mode);
 void       gtk_ctree_set_row_data           (GtkCTree     *ctree,
                                             GList        *node,
                                             gpointer      data);
@@ -307,7 +319,7 @@ void       gtk_ctree_set_row_data_full      (GtkCTree     *ctree,
                                             GtkDestroyNotify destroy);
 gpointer   gtk_ctree_get_row_data           (GtkCTree     *ctree,
                                             GList        *node);
-void       gtk_ctree_scroll_to              (GtkCTree     *ctree,
+void       gtk_ctree_moveto                 (GtkCTree     *ctree,
                                             GList        *node,
                                             gint          column,
                                             gfloat        row_align,
index 2042ae8749c1363ec0e42adef2e40e3198c83137..5f99a8336fe09699ef9a7eef68a6d028e47d11d5 100644 (file)
@@ -3034,12 +3034,14 @@ create_list (void)
     gtk_widget_destroy (window);
 }
 
+
 /*
  * GtkCList
  */
-#define TESTGTK_CLIST_COLUMNS 7
+
+#define TESTGTK_CLIST_COLUMNS 20
 static gint clist_rows = 0;
-static gint clist_selected_row = 0;
+static GtkWidget *clist_omenu;
 
 static void
 add1000_clist (GtkWidget *widget, gpointer data)
@@ -3101,7 +3103,6 @@ add10000_clist (GtkWidget *widget, gpointer data)
       gtk_clist_append (GTK_CLIST (data), texts);
     }
   gtk_clist_thaw (GTK_CLIST (data));
-
 }
 
 void
@@ -3114,7 +3115,7 @@ clear_clist (GtkWidget *widget, gpointer data)
 void
 remove_row_clist (GtkWidget *widget, gpointer data)
 {
-  gtk_clist_remove (GTK_CLIST (data), clist_selected_row);
+  gtk_clist_remove (GTK_CLIST (data), GTK_CLIST (data)->focus_row);
   clist_rows--;
 }
 
@@ -3134,7 +3135,8 @@ void
 select_clist (GtkWidget *widget,
              gint row, 
              gint column, 
-             GdkEventButton * bevent)
+             GdkEventButton * bevent,
+             GtkWidget *button)
 {
   gint i;
   guint8 spacing;
@@ -3187,15 +3189,14 @@ select_clist (GtkWidget *widget,
     }
 
   g_print ("\n\n\n");
-
-  clist_selected_row = row;
 }
 
 void
 unselect_clist (GtkWidget *widget,
                gint row, 
                gint column, 
-               GdkEventButton * bevent)
+               GdkEventButton * bevent,
+               GtkWidget *button)
 {
   gint i;
   guint8 spacing;
@@ -3248,8 +3249,6 @@ unselect_clist (GtkWidget *widget,
     }
 
   g_print ("\n\n\n");
-
-  clist_selected_row = row;
 }
 
 static void
@@ -3257,16 +3256,14 @@ insert_row_clist (GtkWidget *widget, gpointer data)
 {
   static char *text[] =
   {
-    "This",
-    "is",
-    "a",
-    "inserted",
-    "row",
-    "la la la la la",
-    "la la la la"
+    "This", "is", "a", "inserted", "row.",
+    "This", "is", "a", "inserted", "row.",
+    "This", "is", "a", "inserted", "row.",
+    "This", "is", "a", "inserted", "row."
   };
 
-  gtk_clist_insert (GTK_CLIST (data), clist_selected_row, text);
+  gtk_clist_insert (GTK_CLIST (data), GTK_CLIST (data)->focus_row, text);
+
   clist_rows++;
 }
 
@@ -3296,6 +3293,36 @@ clist_warning_test (GtkWidget *button,
   gtk_widget_unref (child);
 }
 
+static void
+undo_selection (GtkWidget *button, GtkCList *clist)
+{
+  gtk_clist_undo_selection (clist);
+}
+
+#define RADIOMENUTOGGLED(_rmi_, __i) { \
+  GSList * __g; \
+  __i = 0; \
+  __g = gtk_radio_menu_item_group(_rmi_); \
+  while( __g  && !((GtkCheckMenuItem *)(__g->data))->active) { \
+    __g = __g->next; \
+    __i++; \
+  }\
+}
+
+static void 
+clist_toggle_sel_mode (GtkWidget *widget, GtkCList *clist)
+{
+  gint i;
+
+  if (!GTK_WIDGET_MAPPED (widget))
+    return;
+
+  RADIOMENUTOGGLED ((GtkRadioMenuItem *)
+                   (((GtkOptionMenu *)clist_omenu)->menu_item), i);
+
+  gtk_clist_set_selection_mode (clist, (GtkSelectionMode) (3-i));
+}
+
 static void
 create_clist (void)
 {
@@ -3310,7 +3337,20 @@ create_clist (void)
     "Title 3",
     "Title 4",
     "Title 5",
-    "Title 6"
+    "Title 6",
+    "Title 7",
+    "Title 8",
+    "Title 9",
+    "Title 10",
+    "Title 11",
+    "Title 12",
+    "Title 13",
+    "Title 14",
+    "Title 15",
+    "Title 16",
+    "Title 17",
+    "Title 18",
+    "Title 19"
   };
 
   char text[TESTGTK_CLIST_COLUMNS][50];
@@ -3322,9 +3362,15 @@ create_clist (void)
   GtkWidget *button;
   GtkWidget *separator;
 
+  GtkWidget *undo_button;
+  GtkWidget *label;
+  GtkWidget *menu;
+  GtkWidget *menu_item;
+  GSList *group;
 
   if (!window)
     {
+      clist_rows = 0;
       window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
 
       gtk_signal_connect (GTK_OBJECT (window), "destroy",
@@ -3334,16 +3380,12 @@ create_clist (void)
       gtk_window_set_title (GTK_WINDOW (window), "clist");
       gtk_container_border_width (GTK_CONTAINER (window), 0);
 
-
       box1 = gtk_vbox_new (FALSE, 0);
       gtk_container_add (GTK_CONTAINER (window), box1);
-      gtk_widget_show (box1);
-
 
       box2 = gtk_hbox_new (FALSE, 10);
       gtk_container_border_width (GTK_CONTAINER (box2), 10);
       gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
-      gtk_widget_show (box2);
 
       /* create GtkCList here so we have a pointer to throw at the 
        * button callbacks -- more is done with it later */
@@ -3359,9 +3401,6 @@ create_clist (void)
                           (GtkSignalFunc) add1000_clist,
                           (gpointer) clist);
 
-      gtk_widget_show (button);
-
-
       button = gtk_button_new_with_label ("Add 10,000 Rows");
       gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
 
@@ -3370,8 +3409,6 @@ create_clist (void)
                           (GtkSignalFunc) add10000_clist,
                           (gpointer) clist);
 
-      gtk_widget_show (button);
-
       button = gtk_button_new_with_label ("Clear List");
       gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
 
@@ -3380,8 +3417,6 @@ create_clist (void)
                           (GtkSignalFunc) clear_clist,
                           (gpointer) clist);
 
-      gtk_widget_show (button);
-
       button = gtk_button_new_with_label ("Remove Row");
       gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
 
@@ -3390,13 +3425,10 @@ create_clist (void)
                           (GtkSignalFunc) remove_row_clist,
                           (gpointer) clist);
 
-      gtk_widget_show (button);
-
       /* second layer of buttons */
       box2 = gtk_hbox_new (FALSE, 10);
       gtk_container_border_width (GTK_CONTAINER (box2), 10);
       gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
-      gtk_widget_show (box2);
 
       button = gtk_button_new_with_label ("Insert Row");
       gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
@@ -3406,8 +3438,6 @@ create_clist (void)
                           (GtkSignalFunc) insert_row_clist,
                           (gpointer) clist);
 
-      gtk_widget_show (button);
-
       button = gtk_button_new_with_label ("Show Title Buttons");
       gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
 
@@ -3416,8 +3446,6 @@ create_clist (void)
                           (GtkSignalFunc) show_titles_clist,
                           (gpointer) clist);
 
-      gtk_widget_show (button);
-
       button = gtk_button_new_with_label ("Hide Title Buttons");
       gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
 
@@ -3426,8 +3454,6 @@ create_clist (void)
                           (GtkSignalFunc) hide_titles_clist,
                           (gpointer) clist);
 
-      gtk_widget_show (button);
-
       button = gtk_button_new_with_label ("Warning Test");
       gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
 
@@ -3436,41 +3462,96 @@ create_clist (void)
                           (GtkSignalFunc) clist_warning_test,
                           (gpointer) clist);
 
-      gtk_widget_show (button);
+      box2 = gtk_hbox_new (FALSE, 10);
+      gtk_container_border_width (GTK_CONTAINER (box2), 10);
+      gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
+
+      undo_button = gtk_button_new_with_label ("Undo last selection");
+      gtk_box_pack_start (GTK_BOX (box2), undo_button, TRUE, TRUE, 0);
+      gtk_signal_connect (GTK_OBJECT (undo_button),
+                          "clicked",
+                          (GtkSignalFunc) undo_selection,
+                          (gpointer) clist);
+
+      label = gtk_label_new ("Selection Mode :");
+      gtk_box_pack_start (GTK_BOX (box2), label, FALSE, TRUE, 0);
+
+      clist_omenu = gtk_option_menu_new ();
+      
+      menu = gtk_menu_new ();
+      group = NULL;
+      
+      menu_item = gtk_radio_menu_item_new_with_label (group, "Single");
+      gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
+                         GTK_SIGNAL_FUNC (clist_toggle_sel_mode), clist);
+      group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (menu_item));
+      gtk_menu_append (GTK_MENU (menu), menu_item);
+      gtk_widget_show (menu_item);
+      
+      menu_item = gtk_radio_menu_item_new_with_label (group, "Browse");
+      gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
+                         GTK_SIGNAL_FUNC (clist_toggle_sel_mode), clist);
+      group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (menu_item));
+      gtk_menu_append (GTK_MENU (menu), menu_item);
+      gtk_widget_show (menu_item);
+      
+      menu_item = gtk_radio_menu_item_new_with_label (group, "Multiple");
+      gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
+                      GTK_SIGNAL_FUNC (clist_toggle_sel_mode), clist);
+      group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (menu_item));
+      gtk_menu_append (GTK_MENU (menu), menu_item);
+      gtk_widget_show (menu_item);
+      
+      menu_item = gtk_radio_menu_item_new_with_label (group, "Extended");
+      gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
+                         GTK_SIGNAL_FUNC (clist_toggle_sel_mode), clist);
+      group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (menu_item));
+      gtk_menu_append (GTK_MENU (menu), menu_item);
+      gtk_check_menu_item_set_state (GTK_CHECK_MENU_ITEM (menu_item), TRUE);
+      gtk_widget_show (menu_item);
+      
+      gtk_option_menu_set_menu (GTK_OPTION_MENU (clist_omenu), menu);
+      gtk_box_pack_start (GTK_BOX (box2), clist_omenu, FALSE, TRUE, 0);
+      
+      gtk_option_menu_set_history (GTK_OPTION_MENU (clist_omenu), 3);
 
       /* vbox for the list itself */
       box2 = gtk_vbox_new (FALSE, 10);
       gtk_container_border_width (GTK_CONTAINER (box2), 10);
       gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0);
-      gtk_widget_show (box2);
 
       /* 
        * the rest of the clist configuration
        */
-      gtk_clist_set_row_height (GTK_CLIST (clist), 20);
-      
+
+      /*
       gtk_signal_connect (GTK_OBJECT (clist), 
                          "select_row",
                          (GtkSignalFunc) select_clist, 
-                         NULL);
+                         undo_button);
 
       gtk_signal_connect (GTK_OBJECT (clist), 
                          "unselect_row",
                          (GtkSignalFunc) unselect_clist, 
-                         NULL);
+                         undo_button);
+      */
+
+      gtk_clist_set_row_height (GTK_CLIST (clist), 18);
+      gtk_widget_set_usize (clist, -1, 300);
 
       gtk_clist_set_column_width (GTK_CLIST (clist), 0, 100);
 
       for (i = 1; i < TESTGTK_CLIST_COLUMNS; i++)
        gtk_clist_set_column_width (GTK_CLIST (clist), i, 80);
 
-      gtk_clist_set_selection_mode (GTK_CLIST (clist), GTK_SELECTION_BROWSE);
-      gtk_clist_set_policy (GTK_CLIST (clist), 
-                           GTK_POLICY_AUTOMATIC,
+      gtk_clist_set_selection_mode (GTK_CLIST (clist), GTK_SELECTION_EXTENDED);
+      gtk_clist_set_policy (GTK_CLIST (clist), GTK_POLICY_AUTOMATIC,
                            GTK_POLICY_AUTOMATIC);
 
-      gtk_clist_set_column_justification (GTK_CLIST (clist), 1, GTK_JUSTIFY_RIGHT);
-      gtk_clist_set_column_justification (GTK_CLIST (clist), 2, GTK_JUSTIFY_CENTER);
+      gtk_clist_set_column_justification (GTK_CLIST (clist), 1,
+                                         GTK_JUSTIFY_RIGHT);
+      gtk_clist_set_column_justification (GTK_CLIST (clist), 2,
+                                         GTK_JUSTIFY_CENTER);
       
       for (i = 0; i < TESTGTK_CLIST_COLUMNS; i++)
        {
@@ -3481,7 +3562,7 @@ create_clist (void)
       sprintf (text[1], "Right");
       sprintf (text[2], "Center");
 
-      for (i = 0; i < 100; i++)
+      for (i = 0; i < 10; i++)
        {
          sprintf (text[0], "Row %d", clist_rows++);
          gtk_clist_append (GTK_CLIST (clist), texts);
@@ -3489,17 +3570,13 @@ create_clist (void)
 
       gtk_container_border_width (GTK_CONTAINER (clist), 5);
       gtk_box_pack_start (GTK_BOX (box2), clist, TRUE, TRUE, 0);
-      gtk_widget_show (clist);
-
 
       separator = gtk_hseparator_new ();
       gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0);
-      gtk_widget_show (separator);
 
       box2 = gtk_vbox_new (FALSE, 10);
       gtk_container_border_width (GTK_CONTAINER (box2), 10);
       gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0);
-      gtk_widget_show (box2);
 
       button = gtk_button_new_with_label ("close");
       gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
@@ -3509,21 +3586,17 @@ create_clist (void)
       gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
       GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
       gtk_widget_grab_default (button);
-
-      gtk_widget_show (button);
     }
 
   if (!GTK_WIDGET_VISIBLE (window))
-    gtk_widget_show (window);
+    gtk_widget_show_all (window);
   else
     {
       clist_rows = 0;
       gtk_widget_destroy (window);
     }
-
 }
 
-
 /*
  * GtkCTree
  */
@@ -3621,26 +3694,6 @@ static GtkWidget *spin2;
 static GtkWidget *spin3;
 static GdkColor  *col_bg;
 
-#define RADIOMENUTOGGLED(_rmi_, __i) { \
-  GSList * __g; \
-  __i = 0; \
-  __g = gtk_radio_menu_item_group(_rmi_); \
-  while( __g  && !((GtkCheckMenuItem *)(__g->data))->active) { \
-    __g = __g->next; \
-    __i++; \
-  }\
-}
-
-#define RADIOBUTTONTOGGLED(_rb_, __i) { \
-  GSList * __g; \
-  __i = 0; \
-  __g = gtk_radio_button_group(_rb_); \
-  while( __g  && !((GtkToggleButton *)(__g->data))->active) { \
-    __g = __g->next; \
-    __i++; \
-  }\
-}
-
 void after_press (GtkCTree *ctree, gpointer data)
 {
   char buf[80];
@@ -3708,7 +3761,7 @@ gint button_press (GtkCTree *ctree, GdkEventButton *event, gpointer data)
          else
            gtk_ctree_expand_recursive (ctree, work);
          after_press (ctree, NULL);
-         gtk_signal_emit_stop_by_name (GTK_OBJECT (ctree), 
+         gtk_signal_emit_stop_by_name (GTK_OBJECT (ctree),
                                        "button_press_event");
        }
       break;
@@ -3907,7 +3960,7 @@ void toggle_sel_mode (GtkWidget *widget, GtkCTree *ctree)
   RADIOMENUTOGGLED ((GtkRadioMenuItem *)
                    (((GtkOptionMenu *)omenu)->menu_item), i);
 
-  gtk_ctree_set_selection_mode (ctree, (GtkSelectionMode) (3-i));
+  gtk_clist_set_selection_mode (GTK_CLIST (ctree), (GtkSelectionMode) (3-i));
   after_press (ctree, NULL);
 }
 
@@ -3996,14 +4049,14 @@ void rebuild_tree (GtkWidget *widget, GtkCTree *ctree)
 
   n = ((pow (b, d) - 1) / (b - 1)) * (p + 1);
 
-  if (n > 200000)
+  if (n > 100000)
     {
       g_print ("%d total items? Try less\n",n);
       return;
     }
 
   gtk_clist_freeze (GTK_CLIST (ctree));
-  gtk_ctree_clear (ctree);
+  gtk_clist_clear (GTK_CLIST (ctree));
 
   books = 1;
   pages = 0;
@@ -4117,9 +4170,20 @@ void create_ctree (void)
                                GTK_SIGNAL_FUNC (after_press), NULL);
       gtk_signal_connect_after (GTK_OBJECT (ctree), "tree_move",
                                GTK_SIGNAL_FUNC (after_move), NULL);
+      gtk_signal_connect_after (GTK_OBJECT (ctree), "end_selection",
+                               GTK_SIGNAL_FUNC (after_press), NULL);
+      gtk_signal_connect_after (GTK_OBJECT (ctree), "toggle_focus_row",
+                               GTK_SIGNAL_FUNC (after_press), NULL);
+      gtk_signal_connect_after (GTK_OBJECT (ctree), "select_all",
+                               GTK_SIGNAL_FUNC (after_press), NULL);
+      gtk_signal_connect_after (GTK_OBJECT (ctree), "unselect_all",
+                               GTK_SIGNAL_FUNC (after_press), NULL);
+      gtk_signal_connect_after (GTK_OBJECT (ctree), "scroll_vertical",
+                               GTK_SIGNAL_FUNC (after_press), NULL);
+      
       gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (ctree), TRUE, TRUE, 0);
       gtk_clist_column_titles_passive (GTK_CLIST (ctree));
-      gtk_clist_set_selection_mode (GTK_CLIST (ctree), GTK_SELECTION_MULTIPLE);
+      gtk_clist_set_selection_mode (GTK_CLIST (ctree), GTK_SELECTION_EXTENDED);
       gtk_clist_set_policy (GTK_CLIST (ctree), GTK_POLICY_ALWAYS, 
                            GTK_POLICY_AUTOMATIC);
       gtk_clist_set_column_width (GTK_CLIST (ctree), 0, 200);
@@ -4299,7 +4363,6 @@ void create_ctree (void)
                       GTK_SIGNAL_FUNC (toggle_sel_mode), ctree);
       group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (menu_item));
       gtk_menu_append (GTK_MENU (menu), menu_item);
-      gtk_check_menu_item_set_state (GTK_CHECK_MENU_ITEM (menu_item), TRUE);
       gtk_widget_show (menu_item);
       
       menu_item = gtk_radio_menu_item_new_with_label (group, "Extended");
@@ -4307,12 +4370,13 @@ void create_ctree (void)
                          GTK_SIGNAL_FUNC (toggle_sel_mode), ctree);
       group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (menu_item));
       gtk_menu_append (GTK_MENU (menu), menu_item);
+      gtk_check_menu_item_set_state (GTK_CHECK_MENU_ITEM (menu_item), TRUE);
       gtk_widget_show (menu_item);
       
       gtk_option_menu_set_menu (GTK_OPTION_MENU (omenu), menu);
       gtk_box_pack_start (GTK_BOX (hbox), omenu, FALSE, TRUE, 0);
       
-      gtk_option_menu_set_history (GTK_OPTION_MENU (omenu), 2);
+      gtk_option_menu_set_history (GTK_OPTION_MENU (omenu), 3);
       
       gtk_widget_realize (window);
       
@@ -4403,7 +4467,6 @@ void create_ctree (void)
     gtk_widget_destroy (window);
 }
 
-
 /*
  * GtkColorSelect
  */
index 2042ae8749c1363ec0e42adef2e40e3198c83137..5f99a8336fe09699ef9a7eef68a6d028e47d11d5 100644 (file)
@@ -3034,12 +3034,14 @@ create_list (void)
     gtk_widget_destroy (window);
 }
 
+
 /*
  * GtkCList
  */
-#define TESTGTK_CLIST_COLUMNS 7
+
+#define TESTGTK_CLIST_COLUMNS 20
 static gint clist_rows = 0;
-static gint clist_selected_row = 0;
+static GtkWidget *clist_omenu;
 
 static void
 add1000_clist (GtkWidget *widget, gpointer data)
@@ -3101,7 +3103,6 @@ add10000_clist (GtkWidget *widget, gpointer data)
       gtk_clist_append (GTK_CLIST (data), texts);
     }
   gtk_clist_thaw (GTK_CLIST (data));
-
 }
 
 void
@@ -3114,7 +3115,7 @@ clear_clist (GtkWidget *widget, gpointer data)
 void
 remove_row_clist (GtkWidget *widget, gpointer data)
 {
-  gtk_clist_remove (GTK_CLIST (data), clist_selected_row);
+  gtk_clist_remove (GTK_CLIST (data), GTK_CLIST (data)->focus_row);
   clist_rows--;
 }
 
@@ -3134,7 +3135,8 @@ void
 select_clist (GtkWidget *widget,
              gint row, 
              gint column, 
-             GdkEventButton * bevent)
+             GdkEventButton * bevent,
+             GtkWidget *button)
 {
   gint i;
   guint8 spacing;
@@ -3187,15 +3189,14 @@ select_clist (GtkWidget *widget,
     }
 
   g_print ("\n\n\n");
-
-  clist_selected_row = row;
 }
 
 void
 unselect_clist (GtkWidget *widget,
                gint row, 
                gint column, 
-               GdkEventButton * bevent)
+               GdkEventButton * bevent,
+               GtkWidget *button)
 {
   gint i;
   guint8 spacing;
@@ -3248,8 +3249,6 @@ unselect_clist (GtkWidget *widget,
     }
 
   g_print ("\n\n\n");
-
-  clist_selected_row = row;
 }
 
 static void
@@ -3257,16 +3256,14 @@ insert_row_clist (GtkWidget *widget, gpointer data)
 {
   static char *text[] =
   {
-    "This",
-    "is",
-    "a",
-    "inserted",
-    "row",
-    "la la la la la",
-    "la la la la"
+    "This", "is", "a", "inserted", "row.",
+    "This", "is", "a", "inserted", "row.",
+    "This", "is", "a", "inserted", "row.",
+    "This", "is", "a", "inserted", "row."
   };
 
-  gtk_clist_insert (GTK_CLIST (data), clist_selected_row, text);
+  gtk_clist_insert (GTK_CLIST (data), GTK_CLIST (data)->focus_row, text);
+
   clist_rows++;
 }
 
@@ -3296,6 +3293,36 @@ clist_warning_test (GtkWidget *button,
   gtk_widget_unref (child);
 }
 
+static void
+undo_selection (GtkWidget *button, GtkCList *clist)
+{
+  gtk_clist_undo_selection (clist);
+}
+
+#define RADIOMENUTOGGLED(_rmi_, __i) { \
+  GSList * __g; \
+  __i = 0; \
+  __g = gtk_radio_menu_item_group(_rmi_); \
+  while( __g  && !((GtkCheckMenuItem *)(__g->data))->active) { \
+    __g = __g->next; \
+    __i++; \
+  }\
+}
+
+static void 
+clist_toggle_sel_mode (GtkWidget *widget, GtkCList *clist)
+{
+  gint i;
+
+  if (!GTK_WIDGET_MAPPED (widget))
+    return;
+
+  RADIOMENUTOGGLED ((GtkRadioMenuItem *)
+                   (((GtkOptionMenu *)clist_omenu)->menu_item), i);
+
+  gtk_clist_set_selection_mode (clist, (GtkSelectionMode) (3-i));
+}
+
 static void
 create_clist (void)
 {
@@ -3310,7 +3337,20 @@ create_clist (void)
     "Title 3",
     "Title 4",
     "Title 5",
-    "Title 6"
+    "Title 6",
+    "Title 7",
+    "Title 8",
+    "Title 9",
+    "Title 10",
+    "Title 11",
+    "Title 12",
+    "Title 13",
+    "Title 14",
+    "Title 15",
+    "Title 16",
+    "Title 17",
+    "Title 18",
+    "Title 19"
   };
 
   char text[TESTGTK_CLIST_COLUMNS][50];
@@ -3322,9 +3362,15 @@ create_clist (void)
   GtkWidget *button;
   GtkWidget *separator;
 
+  GtkWidget *undo_button;
+  GtkWidget *label;
+  GtkWidget *menu;
+  GtkWidget *menu_item;
+  GSList *group;
 
   if (!window)
     {
+      clist_rows = 0;
       window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
 
       gtk_signal_connect (GTK_OBJECT (window), "destroy",
@@ -3334,16 +3380,12 @@ create_clist (void)
       gtk_window_set_title (GTK_WINDOW (window), "clist");
       gtk_container_border_width (GTK_CONTAINER (window), 0);
 
-
       box1 = gtk_vbox_new (FALSE, 0);
       gtk_container_add (GTK_CONTAINER (window), box1);
-      gtk_widget_show (box1);
-
 
       box2 = gtk_hbox_new (FALSE, 10);
       gtk_container_border_width (GTK_CONTAINER (box2), 10);
       gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
-      gtk_widget_show (box2);
 
       /* create GtkCList here so we have a pointer to throw at the 
        * button callbacks -- more is done with it later */
@@ -3359,9 +3401,6 @@ create_clist (void)
                           (GtkSignalFunc) add1000_clist,
                           (gpointer) clist);
 
-      gtk_widget_show (button);
-
-
       button = gtk_button_new_with_label ("Add 10,000 Rows");
       gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
 
@@ -3370,8 +3409,6 @@ create_clist (void)
                           (GtkSignalFunc) add10000_clist,
                           (gpointer) clist);
 
-      gtk_widget_show (button);
-
       button = gtk_button_new_with_label ("Clear List");
       gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
 
@@ -3380,8 +3417,6 @@ create_clist (void)
                           (GtkSignalFunc) clear_clist,
                           (gpointer) clist);
 
-      gtk_widget_show (button);
-
       button = gtk_button_new_with_label ("Remove Row");
       gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
 
@@ -3390,13 +3425,10 @@ create_clist (void)
                           (GtkSignalFunc) remove_row_clist,
                           (gpointer) clist);
 
-      gtk_widget_show (button);
-
       /* second layer of buttons */
       box2 = gtk_hbox_new (FALSE, 10);
       gtk_container_border_width (GTK_CONTAINER (box2), 10);
       gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
-      gtk_widget_show (box2);
 
       button = gtk_button_new_with_label ("Insert Row");
       gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
@@ -3406,8 +3438,6 @@ create_clist (void)
                           (GtkSignalFunc) insert_row_clist,
                           (gpointer) clist);
 
-      gtk_widget_show (button);
-
       button = gtk_button_new_with_label ("Show Title Buttons");
       gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
 
@@ -3416,8 +3446,6 @@ create_clist (void)
                           (GtkSignalFunc) show_titles_clist,
                           (gpointer) clist);
 
-      gtk_widget_show (button);
-
       button = gtk_button_new_with_label ("Hide Title Buttons");
       gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
 
@@ -3426,8 +3454,6 @@ create_clist (void)
                           (GtkSignalFunc) hide_titles_clist,
                           (gpointer) clist);
 
-      gtk_widget_show (button);
-
       button = gtk_button_new_with_label ("Warning Test");
       gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
 
@@ -3436,41 +3462,96 @@ create_clist (void)
                           (GtkSignalFunc) clist_warning_test,
                           (gpointer) clist);
 
-      gtk_widget_show (button);
+      box2 = gtk_hbox_new (FALSE, 10);
+      gtk_container_border_width (GTK_CONTAINER (box2), 10);
+      gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
+
+      undo_button = gtk_button_new_with_label ("Undo last selection");
+      gtk_box_pack_start (GTK_BOX (box2), undo_button, TRUE, TRUE, 0);
+      gtk_signal_connect (GTK_OBJECT (undo_button),
+                          "clicked",
+                          (GtkSignalFunc) undo_selection,
+                          (gpointer) clist);
+
+      label = gtk_label_new ("Selection Mode :");
+      gtk_box_pack_start (GTK_BOX (box2), label, FALSE, TRUE, 0);
+
+      clist_omenu = gtk_option_menu_new ();
+      
+      menu = gtk_menu_new ();
+      group = NULL;
+      
+      menu_item = gtk_radio_menu_item_new_with_label (group, "Single");
+      gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
+                         GTK_SIGNAL_FUNC (clist_toggle_sel_mode), clist);
+      group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (menu_item));
+      gtk_menu_append (GTK_MENU (menu), menu_item);
+      gtk_widget_show (menu_item);
+      
+      menu_item = gtk_radio_menu_item_new_with_label (group, "Browse");
+      gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
+                         GTK_SIGNAL_FUNC (clist_toggle_sel_mode), clist);
+      group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (menu_item));
+      gtk_menu_append (GTK_MENU (menu), menu_item);
+      gtk_widget_show (menu_item);
+      
+      menu_item = gtk_radio_menu_item_new_with_label (group, "Multiple");
+      gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
+                      GTK_SIGNAL_FUNC (clist_toggle_sel_mode), clist);
+      group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (menu_item));
+      gtk_menu_append (GTK_MENU (menu), menu_item);
+      gtk_widget_show (menu_item);
+      
+      menu_item = gtk_radio_menu_item_new_with_label (group, "Extended");
+      gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
+                         GTK_SIGNAL_FUNC (clist_toggle_sel_mode), clist);
+      group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (menu_item));
+      gtk_menu_append (GTK_MENU (menu), menu_item);
+      gtk_check_menu_item_set_state (GTK_CHECK_MENU_ITEM (menu_item), TRUE);
+      gtk_widget_show (menu_item);
+      
+      gtk_option_menu_set_menu (GTK_OPTION_MENU (clist_omenu), menu);
+      gtk_box_pack_start (GTK_BOX (box2), clist_omenu, FALSE, TRUE, 0);
+      
+      gtk_option_menu_set_history (GTK_OPTION_MENU (clist_omenu), 3);
 
       /* vbox for the list itself */
       box2 = gtk_vbox_new (FALSE, 10);
       gtk_container_border_width (GTK_CONTAINER (box2), 10);
       gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0);
-      gtk_widget_show (box2);
 
       /* 
        * the rest of the clist configuration
        */
-      gtk_clist_set_row_height (GTK_CLIST (clist), 20);
-      
+
+      /*
       gtk_signal_connect (GTK_OBJECT (clist), 
                          "select_row",
                          (GtkSignalFunc) select_clist, 
-                         NULL);
+                         undo_button);
 
       gtk_signal_connect (GTK_OBJECT (clist), 
                          "unselect_row",
                          (GtkSignalFunc) unselect_clist, 
-                         NULL);
+                         undo_button);
+      */
+
+      gtk_clist_set_row_height (GTK_CLIST (clist), 18);
+      gtk_widget_set_usize (clist, -1, 300);
 
       gtk_clist_set_column_width (GTK_CLIST (clist), 0, 100);
 
       for (i = 1; i < TESTGTK_CLIST_COLUMNS; i++)
        gtk_clist_set_column_width (GTK_CLIST (clist), i, 80);
 
-      gtk_clist_set_selection_mode (GTK_CLIST (clist), GTK_SELECTION_BROWSE);
-      gtk_clist_set_policy (GTK_CLIST (clist), 
-                           GTK_POLICY_AUTOMATIC,
+      gtk_clist_set_selection_mode (GTK_CLIST (clist), GTK_SELECTION_EXTENDED);
+      gtk_clist_set_policy (GTK_CLIST (clist), GTK_POLICY_AUTOMATIC,
                            GTK_POLICY_AUTOMATIC);
 
-      gtk_clist_set_column_justification (GTK_CLIST (clist), 1, GTK_JUSTIFY_RIGHT);
-      gtk_clist_set_column_justification (GTK_CLIST (clist), 2, GTK_JUSTIFY_CENTER);
+      gtk_clist_set_column_justification (GTK_CLIST (clist), 1,
+                                         GTK_JUSTIFY_RIGHT);
+      gtk_clist_set_column_justification (GTK_CLIST (clist), 2,
+                                         GTK_JUSTIFY_CENTER);
       
       for (i = 0; i < TESTGTK_CLIST_COLUMNS; i++)
        {
@@ -3481,7 +3562,7 @@ create_clist (void)
       sprintf (text[1], "Right");
       sprintf (text[2], "Center");
 
-      for (i = 0; i < 100; i++)
+      for (i = 0; i < 10; i++)
        {
          sprintf (text[0], "Row %d", clist_rows++);
          gtk_clist_append (GTK_CLIST (clist), texts);
@@ -3489,17 +3570,13 @@ create_clist (void)
 
       gtk_container_border_width (GTK_CONTAINER (clist), 5);
       gtk_box_pack_start (GTK_BOX (box2), clist, TRUE, TRUE, 0);
-      gtk_widget_show (clist);
-
 
       separator = gtk_hseparator_new ();
       gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0);
-      gtk_widget_show (separator);
 
       box2 = gtk_vbox_new (FALSE, 10);
       gtk_container_border_width (GTK_CONTAINER (box2), 10);
       gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0);
-      gtk_widget_show (box2);
 
       button = gtk_button_new_with_label ("close");
       gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
@@ -3509,21 +3586,17 @@ create_clist (void)
       gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
       GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
       gtk_widget_grab_default (button);
-
-      gtk_widget_show (button);
     }
 
   if (!GTK_WIDGET_VISIBLE (window))
-    gtk_widget_show (window);
+    gtk_widget_show_all (window);
   else
     {
       clist_rows = 0;
       gtk_widget_destroy (window);
     }
-
 }
 
-
 /*
  * GtkCTree
  */
@@ -3621,26 +3694,6 @@ static GtkWidget *spin2;
 static GtkWidget *spin3;
 static GdkColor  *col_bg;
 
-#define RADIOMENUTOGGLED(_rmi_, __i) { \
-  GSList * __g; \
-  __i = 0; \
-  __g = gtk_radio_menu_item_group(_rmi_); \
-  while( __g  && !((GtkCheckMenuItem *)(__g->data))->active) { \
-    __g = __g->next; \
-    __i++; \
-  }\
-}
-
-#define RADIOBUTTONTOGGLED(_rb_, __i) { \
-  GSList * __g; \
-  __i = 0; \
-  __g = gtk_radio_button_group(_rb_); \
-  while( __g  && !((GtkToggleButton *)(__g->data))->active) { \
-    __g = __g->next; \
-    __i++; \
-  }\
-}
-
 void after_press (GtkCTree *ctree, gpointer data)
 {
   char buf[80];
@@ -3708,7 +3761,7 @@ gint button_press (GtkCTree *ctree, GdkEventButton *event, gpointer data)
          else
            gtk_ctree_expand_recursive (ctree, work);
          after_press (ctree, NULL);
-         gtk_signal_emit_stop_by_name (GTK_OBJECT (ctree), 
+         gtk_signal_emit_stop_by_name (GTK_OBJECT (ctree),
                                        "button_press_event");
        }
       break;
@@ -3907,7 +3960,7 @@ void toggle_sel_mode (GtkWidget *widget, GtkCTree *ctree)
   RADIOMENUTOGGLED ((GtkRadioMenuItem *)
                    (((GtkOptionMenu *)omenu)->menu_item), i);
 
-  gtk_ctree_set_selection_mode (ctree, (GtkSelectionMode) (3-i));
+  gtk_clist_set_selection_mode (GTK_CLIST (ctree), (GtkSelectionMode) (3-i));
   after_press (ctree, NULL);
 }
 
@@ -3996,14 +4049,14 @@ void rebuild_tree (GtkWidget *widget, GtkCTree *ctree)
 
   n = ((pow (b, d) - 1) / (b - 1)) * (p + 1);
 
-  if (n > 200000)
+  if (n > 100000)
     {
       g_print ("%d total items? Try less\n",n);
       return;
     }
 
   gtk_clist_freeze (GTK_CLIST (ctree));
-  gtk_ctree_clear (ctree);
+  gtk_clist_clear (GTK_CLIST (ctree));
 
   books = 1;
   pages = 0;
@@ -4117,9 +4170,20 @@ void create_ctree (void)
                                GTK_SIGNAL_FUNC (after_press), NULL);
       gtk_signal_connect_after (GTK_OBJECT (ctree), "tree_move",
                                GTK_SIGNAL_FUNC (after_move), NULL);
+      gtk_signal_connect_after (GTK_OBJECT (ctree), "end_selection",
+                               GTK_SIGNAL_FUNC (after_press), NULL);
+      gtk_signal_connect_after (GTK_OBJECT (ctree), "toggle_focus_row",
+                               GTK_SIGNAL_FUNC (after_press), NULL);
+      gtk_signal_connect_after (GTK_OBJECT (ctree), "select_all",
+                               GTK_SIGNAL_FUNC (after_press), NULL);
+      gtk_signal_connect_after (GTK_OBJECT (ctree), "unselect_all",
+                               GTK_SIGNAL_FUNC (after_press), NULL);
+      gtk_signal_connect_after (GTK_OBJECT (ctree), "scroll_vertical",
+                               GTK_SIGNAL_FUNC (after_press), NULL);
+      
       gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (ctree), TRUE, TRUE, 0);
       gtk_clist_column_titles_passive (GTK_CLIST (ctree));
-      gtk_clist_set_selection_mode (GTK_CLIST (ctree), GTK_SELECTION_MULTIPLE);
+      gtk_clist_set_selection_mode (GTK_CLIST (ctree), GTK_SELECTION_EXTENDED);
       gtk_clist_set_policy (GTK_CLIST (ctree), GTK_POLICY_ALWAYS, 
                            GTK_POLICY_AUTOMATIC);
       gtk_clist_set_column_width (GTK_CLIST (ctree), 0, 200);
@@ -4299,7 +4363,6 @@ void create_ctree (void)
                       GTK_SIGNAL_FUNC (toggle_sel_mode), ctree);
       group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (menu_item));
       gtk_menu_append (GTK_MENU (menu), menu_item);
-      gtk_check_menu_item_set_state (GTK_CHECK_MENU_ITEM (menu_item), TRUE);
       gtk_widget_show (menu_item);
       
       menu_item = gtk_radio_menu_item_new_with_label (group, "Extended");
@@ -4307,12 +4370,13 @@ void create_ctree (void)
                          GTK_SIGNAL_FUNC (toggle_sel_mode), ctree);
       group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (menu_item));
       gtk_menu_append (GTK_MENU (menu), menu_item);
+      gtk_check_menu_item_set_state (GTK_CHECK_MENU_ITEM (menu_item), TRUE);
       gtk_widget_show (menu_item);
       
       gtk_option_menu_set_menu (GTK_OPTION_MENU (omenu), menu);
       gtk_box_pack_start (GTK_BOX (hbox), omenu, FALSE, TRUE, 0);
       
-      gtk_option_menu_set_history (GTK_OPTION_MENU (omenu), 2);
+      gtk_option_menu_set_history (GTK_OPTION_MENU (omenu), 3);
       
       gtk_widget_realize (window);
       
@@ -4403,7 +4467,6 @@ void create_ctree (void)
     gtk_widget_destroy (window);
 }
 
-
 /*
  * GtkColorSelect
  */